¿Qué es el OWASP ML Security Top 10?

El OWASP Machine Learning Security Top 10 identifica los riesgos de seguridad más significativos específicos de los sistemas de machine learning. A diferencia del software tradicional, los sistemas ML son vulnerables a ataques únicos dirigidos a datos de entrenamiento, componentes internos del modelo y pipelines de inferencia. Esta guía cubre ataques adversarios, envenenamiento de datos, robo de modelos, riesgos de cadena de suministro y más, con ejemplos prácticos de código Python.

1�E�⃣ ML01 - Ataque de Manipulación de Entrada

Critical

Descripción General

Los ataques de manipulación de entrada (ataques adversarios) elaboran entradas especialmente diseñadas para hacer que los modelos ML realicen predicciones incorrectas. Perturbaciones pequeñas, a menudo imperceptibles, en imágenes, texto u otras entradas pueden engañar a clasificadores, eludir sistemas de detección y evitar filtros de contenido. Este es el vector de ataque más conocido específico de ML.

Riesgo

Los ejemplos adversarios pueden eludir sistemas ML críticos para la seguridad: percepción de vehículos autónomos, detección de malware, detección de fraude y moderación de contenido. Un atacante puede hacer que una señal de alto sea clasificada como una señal de límite de velocidad, o hacer que el malware parezca benigno para un antivirus basado en ML.

Ejemplo de Código Vulnerable

Python ❁EBad
import numpy as np
from tensorflow import keras

# Model with no adversarial robustness
model = keras.models.load_model("classifier.h5")

def predict(image):
    # Direct prediction  Eno input validation or preprocessing
    result = model.predict(np.expand_dims(image, axis=0))
    return np.argmax(result)
    # No confidence threshold check
    # No input bounds validation
    # Vulnerable to FGSM, PGD, C&W attacks

Ejemplo de Código Seguro

Python ✁EGood
import numpy as np
from tensorflow import keras
from art.defences.preprocessor import SpatialSmoothing
from art.defences.detector.evasion import BinaryInputDetector

# Load adversarially trained model
model = keras.models.load_model("classifier_robust.h5")

# Input preprocessing to remove perturbations
smoother = SpatialSmoothing(window_size=3)
detector = BinaryInputDetector(model)

def predict_secure(image):
    # Validate input bounds
    if image.min() < 0 or image.max() > 1:
        raise ValueError("Input out of expected range")

    # Detect adversarial input
    if detector.detect(image):
        raise ValueError("Adversarial input detected")

    # Apply spatial smoothing defense
    cleaned = smoother(image)[0]
    result = model.predict(np.expand_dims(cleaned, axis=0))

    # Reject low-confidence predictions
    confidence = np.max(result)
    if confidence < 0.85:
        return {"label": "uncertain", "confidence": confidence}
    return {"label": np.argmax(result), "confidence": confidence}

Lista de Verificación de Mitigación

2�E�⃣ ML02 - Ataque de Envenenamiento de Datos

Critical

Descripción General

Los ataques de envenenamiento de datos inyectan muestras maliciosas en conjuntos de datos de entrenamiento para corromper el comportamiento aprendido del modelo. Los atacantes pueden introducir puertas traseras (patrones de activación que causan clasificaciones erróneas específicas), desplazar límites de decisión o degradar la precisión general del modelo. Esto es especialmente peligroso cuando los datos de entrenamiento se obtienen de internet o contenido generado por usuarios.

Riesgo

Un modelo envenenado puede comportarse normalmente con entradas limpias pero clasificar incorrectamente cuando está presente un patrón de activación específico. Por ejemplo, un clasificador de malware con puerta trasera podría aprobar cualquier muestra de malware que contenga una secuencia de bytes específica. El ataque es sigiloso porque la precisión del modelo en datos limpios permanece alta.

Ejemplo de Código Vulnerable

Python ❁EBad
import pandas as pd
from sklearn.ensemble import RandomForestClassifier

# Training on unvalidated, crowdsourced data
data = pd.read_csv("user_submitted_data.csv")  # No validation!

# No outlier detection or data quality checks
X = data.drop("label", axis=1)
y = data["label"]

model = RandomForestClassifier()
model.fit(X, y)  # Training directly on untrusted data!

# No comparison against clean baseline
# No data provenance tracking

Ejemplo de Código Seguro

Python ✁EGood
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier, IsolationForest
from sklearn.model_selection import cross_val_score

# Load data with provenance tracking
data = pd.read_csv("training_data.csv")
data_hash = hashlib.sha256(data.to_csv().encode()).hexdigest()
log.info(f"Training data hash: {data_hash}")

X = data.drop("label", axis=1)
y = data["label"]

# Detect and remove anomalous samples
iso_forest = IsolationForest(contamination=0.05, random_state=42)
outlier_mask = iso_forest.fit_predict(X) == 1
X_clean, y_clean = X[outlier_mask], y[outlier_mask]
log.info(f"Removed {(~outlier_mask).sum()} outliers from {len(X)} samples")

# Train and validate against baseline
model = RandomForestClassifier(random_state=42)
scores = cross_val_score(model, X_clean, y_clean, cv=5)
if scores.mean() < BASELINE_ACCURACY - 0.05:
    raise ValueError("Model accuracy dropped  Epossible data poisoning")

model.fit(X_clean, y_clean)

Lista de Verificación de Mitigación

3�E�⃣ ML03 - Ataque de Inversión de Modelo

High

Descripción General

Los ataques de inversión de modelo reconstruyen datos de entrenamiento sensibles consultando el modelo y analizando sus salidas. Un atacante puede recuperar información privada como rostros, registros médicos o datos personales utilizados durante el entrenamiento. Esto es particularmente preocupante para modelos entrenados con conjuntos de datos sensibles (salud, biometría, datos financieros).

Riesgo

La inversión de modelo puede violar regulaciones de privacidad de datos (GDPR, HIPAA) al exponer información de identificación personal de los datos de entrenamiento. Un atacante con acceso API a un modelo de reconocimiento facial podría reconstruir rostros de individuos en el conjunto de entrenamiento.

Ejemplo de Código Vulnerable

Python (API) ❁EBad
from flask import Flask, request, jsonify

app = Flask(__name__)
model = load_model("face_classifier.h5")

@app.route("/predict", methods=["POST"])
def predict():
    image = request.files["image"]
    result = model.predict(preprocess(image))
    # Returns full probability vector  Eenables model inversion!
    return jsonify({
        "probabilities": result.tolist(),  # All class probabilities!
        "prediction": int(np.argmax(result)),
        "confidence": float(np.max(result))
    })
    # No rate limiting, no query logging
    # Unlimited API access for gradient estimation

Ejemplo de Código Seguro

Python (API) ✁EGood
from flask import Flask, request, jsonify
from flask_limiter import Limiter
import numpy as np

app = Flask(__name__)
limiter = Limiter(app, default_limits=["100/hour"])
model = load_model("face_classifier_dp.h5")  # Trained with DP

@app.route("/predict", methods=["POST"])
@limiter.limit("100/hour")
def predict():
    image = request.files["image"]
    result = model.predict(preprocess(image))

    # Return only top-1 prediction  Eno probability vector
    prediction = int(np.argmax(result))
    log_query(request.remote_addr, prediction)  # Audit logging

    return jsonify({
        "prediction": prediction
        # No probabilities, no confidence scores
    })

Lista de Verificación de Mitigación

4�E�⃣ ML04 - Ataque de Inferencia de Pertenencia

High

Descripción General

Los ataques de inferencia de pertenencia determinan si un punto de datos específico se utilizó en el conjunto de datos de entrenamiento del modelo. Al analizar las puntuaciones de confianza del modelo y el comportamiento en entradas conocidas vs. desconocidas, los atacantes pueden inferir información de pertenencia privada. Esta es una amenaza significativa a la privacidad para modelos entrenados con datos sensibles.

Riesgo

La inferencia de pertenencia puede revelar que los datos de un individuo específico se utilizaron para el entrenamiento, por ejemplo, confirmando que el registro de un paciente estaba en un conjunto de datos clínicos, o que el rostro de una persona se utilizó para entrenamiento de vigilancia. Esto viola las expectativas de privacidad y potencialmente regulaciones como GDPR.

Ejemplo de Código Vulnerable

Python ❁EBad
from sklearn.neural_network import MLPClassifier

# Overfitted model  Ememorizes training data
model = MLPClassifier(
    hidden_layer_sizes=(512, 512, 256),  # Over-parameterized!
    max_iter=1000,
    # No regularization
    # No early stopping
)
model.fit(X_train, y_train)

# Model memorizes training data ↁEmembership inference possible
# Training accuracy: 99.9% vs Test accuracy: 82%
# This gap indicates overfitting = information leakage

def predict_with_confidence(x):
    proba = model.predict_proba([x])[0]
    return {"probabilities": proba.tolist()}  # Leaks membership info!

Ejemplo de Código Seguro

Python ✁EGood
from sklearn.neural_network import MLPClassifier
import numpy as np

# Regularized model with early stopping to reduce overfitting
model = MLPClassifier(
    hidden_layer_sizes=(128, 64),
    max_iter=500,
    alpha=0.01,               # L2 regularization
    early_stopping=True,       # Prevents memorization
    validation_fraction=0.15,
)
model.fit(X_train, y_train)

# Verify train/test gap is small (low overfitting)
train_acc = model.score(X_train, y_train)
test_acc = model.score(X_test, y_test)
assert train_acc - test_acc < 0.05, "Overfitting detected!"

def predict_secure(x):
    pred = model.predict([x])[0]
    return {"prediction": int(pred)}  # Label only, no probabilities

Lista de Verificación de Mitigación

5�E�⃣ ML05 - Robo de Modelo

Critical

Descripción General

Los ataques de robo de modelo (extracción de modelo) crean una copia funcional de un modelo ML propietario consultándolo sistemáticamente y entrenando un modelo sustituto en los pares entrada-salida. El modelo robado puede luego usarse para encontrar ejemplos adversarios, competir comercialmente o hacer ingeniería inversa de los datos de entrenamiento del modelo.

Riesgo

Un modelo robado representa pérdida de propiedad intelectual y ventaja competitiva. El modelo extraído puede usarse sin conexión para elaborar ataques adversarios o para entender límites de decisión. Millones de dólares de inversión en entrenamiento pueden replicarse con miles de consultas API.

Ejemplo de Código Vulnerable

Python (API) ❁EBad
from flask import Flask, request, jsonify

app = Flask(__name__)
model = load_proprietary_model()

@app.route("/predict", methods=["POST"])
def predict():
    data = request.json["features"]
    result = model.predict_proba([data])[0]

    # Returns full probability distribution
    return jsonify({
        "probabilities": result.tolist(),
        "prediction": int(np.argmax(result))
    })
    # No rate limiting  Eunlimited queries
    # No anomaly detection on query patterns
    # Attacker can extract model with ~10K queries

Ejemplo de Código Seguro

Python (API) ✁EGood
from flask import Flask, request, jsonify
from flask_limiter import Limiter
import numpy as np

app = Flask(__name__)
limiter = Limiter(app, default_limits=["50/hour"])

# Watermarked model for theft detection
model = load_watermarked_model()
query_monitor = QueryPatternDetector()

@app.route("/predict", methods=["POST"])
@limiter.limit("50/hour")
def predict():
    data = request.json["features"]
    api_key = request.headers.get("X-API-Key")

    # Detect extraction patterns (uniform sampling, grid queries)
    if query_monitor.is_suspicious(api_key, data):
        log_alert(f"Possible extraction: {api_key}")
        return jsonify({"error": "rate limited"}), 429

    result = model.predict([data])[0]
    return jsonify({
        "prediction": int(result)  # Label only, no probabilities
    })

Lista de Verificación de Mitigación

6�E�⃣ ML06 - Ataques a la Cadena de Suministro de IA

Critical

Descripción General

Los ataques a la cadena de suministro de IA se dirigen al pipeline de desarrollo de ML: modelos preentrenados de hubs de modelos, conjuntos de datos de terceros, frameworks ML y dependencias. Los modelos maliciosos pueden contener puertas traseras ocultas, y las bibliotecas comprometidas pueden inyectar vulnerabilidades. Los formatos de serialización utilizados por frameworks ML (Pickle, SavedModel) pueden ejecutar código arbitrario al cargarse.

Riesgo

Cargar un archivo de modelo malicioso puede ejecutar código arbitrario (ataques de deserialización Pickle). Los modelos preentrenados de fuentes no confiables pueden contener puertas traseras. Las bibliotecas ML comprometidas afectan a todos los usuarios descendientes. La cadena de suministro de ML tiene menos controles de seguridad que las cadenas de suministro de software tradicionales.

Ejemplo de Código Vulnerable

Python ❁EBad
import pickle
import torch

# Loading untrusted model  Earbitrary code execution!
with open("model_from_internet.pkl", "rb") as f:
    model = pickle.load(f)  # DANGEROUS: can execute any code!

# Loading unverified PyTorch model
model = torch.load("untrusted_model.pt")  # Uses pickle internally!

# Using unvetted model from public hub
from transformers import AutoModel
model = AutoModel.from_pretrained("random-user/suspicious-model")
# No hash verification, no security scan

Ejemplo de Código Seguro

Python ✁EGood
import torch
import hashlib
from safetensors.torch import load_file

# Use SafeTensors  Eno arbitrary code execution
model_state = load_file("model.safetensors")  # Safe format!
model = MyModel()
model.load_state_dict(model_state)

# Verify model hash before loading
EXPECTED_HASH = "sha256:a1b2c3d4..."
with open("model.safetensors", "rb") as f:
    actual_hash = "sha256:" + hashlib.sha256(f.read()).hexdigest()
assert actual_hash == EXPECTED_HASH, "Model integrity check failed!"

# Use trusted models from verified organizations
from transformers import AutoModel
model = AutoModel.from_pretrained(
    "google/bert-base-uncased",  # Verified organization
    revision="a265f77",           # Pin to specific commit
)

Lista de Verificación de Mitigación

7�E�⃣ ML07 - Ataque de Transfer Learning

High

Descripción General

Los ataques de transfer learning explotan la práctica común de afinar modelos preentrenados. Las puertas traseras incorporadas en el modelo base persisten a través del ajuste fino y permanecen activas en el modelo descendiente. Un atacante que publique un modelo preentrenado popular puede comprometer todas las aplicaciones que lo usan como base.

Riesgo

Las puertas traseras en modelos preentrenados sobreviven al ajuste fino porque están incorporadas en capas profundas que a menudo se congelan durante el transfer learning. Un solo modelo base comprometido puede afectar a miles de aplicaciones descendientes. El ataque es escalable y difícil de detectar.

Ejemplo de Código Vulnerable

Python ❁EBad
from transformers import AutoModelForSequenceClassification

# Fine-tuning an unvetted pre-trained model
model = AutoModelForSequenceClassification.from_pretrained(
    "unknown-user/bert-finetuned-sentiment",  # Untrusted source!
    num_labels=2
)

# Freezing base layers  Epreserves any hidden backdoor
for param in model.base_model.parameters():
    param.requires_grad = False  # Backdoor in frozen layers persists!

# Fine-tune only the classification head
trainer.train()  # Backdoor remains undetected

Ejemplo de Código Seguro

Python ✁EGood
from transformers import AutoModelForSequenceClassification
from neural_cleanse import BackdoorDetector

# Use only verified, trusted base models
model = AutoModelForSequenceClassification.from_pretrained(
    "google/bert-base-uncased",  # Trusted source
    num_labels=2,
    revision="main",
)

# Scan pre-trained model for backdoors before fine-tuning
detector = BackdoorDetector(model)
if detector.scan():
    raise SecurityError("Potential backdoor detected in base model")

# Fine-tune ALL layers (not just head) to overwrite potential backdoors
for param in model.parameters():
    param.requires_grad = True  # Train all layers

# Validate with clean test set + trigger test set
trainer.train()
evaluate_for_backdoors(model, trigger_test_set)

Lista de Verificación de Mitigación

8�E�⃣ ML08 - Sesgo de Modelo

Medium

Descripción General

El sesgo de modelo ocurre cuando la distribución de datos en producción difiere significativamente de la distribución de datos de entrenamiento (sesgo entrenamiento-servicio). Esto puede suceder naturalmente con el tiempo (deriva de datos) o ser causado intencionalmente por atacantes que manipulan la distribución de entrada de producción para degradar el rendimiento del modelo o sesgar predicciones.

Riesgo

El sesgo de modelo causa fallos silenciosos donde el modelo produce predicciones incorrectas pero confiadas. En sistemas financieros, los atacantes pueden explotar el sesgo para eludir la detección de fraude. En sistemas de recomendación, el sesgo puede inducirse intencionalmente para promover contenido o productos específicos.

Ejemplo de Código Vulnerable

Python ❁EBad
import joblib

# Deploy model with no drift monitoring
model = joblib.load("model_trained_2023.pkl")

def predict(features):
    # No check if input distribution has changed
    # No feature validation against training schema
    return model.predict([features])[0]
    # Model may be months/years old
    # No monitoring of prediction distribution
    # Silent degradation goes undetected

Ejemplo de Código Seguro

Python ✁EGood
import joblib
import numpy as np
from scipy import stats
from evidently import ColumnDriftMetric

model = joblib.load("model.pkl")
training_stats = joblib.load("training_stats.pkl")

def predict_with_monitoring(features):
    # Validate feature schema and ranges
    for i, (val, stat) in enumerate(zip(features, training_stats)):
        z_score = abs((val - stat["mean"]) / stat["std"])
        if z_score > 5:
            log.warning(f"Feature {i} out of distribution: z={z_score:.1f}")

    prediction = model.predict([features])[0]

    # Log prediction distribution for drift monitoring
    metrics_collector.log(features, prediction)

    # Periodic drift detection (run by monitoring job)
    # drift_report = ColumnDriftMetric().calculate(reference, current)
    # Alert if drift detected ↁEtrigger retraining

    return prediction

Lista de Verificación de Mitigación

9�E�⃣ ML09 - Ataque de Integridad de Salida

High

Descripción General

Los ataques de integridad de salida manipulan las predicciones del modelo después de que salen del modelo pero antes de que lleguen a la aplicación consumidora. Esto incluye ataques de intermediario en APIs de predicción, manipulación de infraestructura de servicio de modelos y manipulación de predicciones en caché. El ataque se dirige al pipeline de inferencia en lugar del modelo en sí.

Riesgo

Las predicciones manipuladas pueden causar decisiones incorrectas en sistemas descendientes: aprobar transacciones fraudulentas, diagnosticar erróneamente condiciones médicas o anular sistemas de seguridad. Debido a que el modelo en sí no está comprometido, el monitoreo estándar del modelo no detectará el ataque.

Ejemplo de Código Vulnerable

Python ❁EBad
import requests

# Consuming model predictions over unencrypted HTTP
def get_prediction(features):
    response = requests.post(
        "http://ml-service/predict",  # HTTP, not HTTPS!
        json={"features": features}
    )
    result = response.json()
    # No integrity verification of the response
    # No validation of prediction format
    return result["prediction"]  # Could be tampered!

Ejemplo de Código Seguro

Python ✁EGood
import requests
import hmac
import hashlib

def get_prediction(features):
    response = requests.post(
        "https://ml-service/predict",  # HTTPS (TLS)
        json={"features": features},
        headers={"Authorization": f"Bearer {API_TOKEN}"},
        verify=True  # Verify TLS certificate
    )
    result = response.json()

    # Verify response integrity with HMAC signature
    signature = response.headers.get("X-Signature")
    expected = hmac.new(
        SHARED_SECRET, str(result).encode(), hashlib.sha256
    ).hexdigest()
    if not hmac.compare_digest(signature, expected):
        raise IntegrityError("Response signature mismatch!")

    # Validate prediction is within expected range
    pred = result["prediction"]
    if pred not in VALID_LABELS:
        raise ValueError(f"Unexpected prediction: {pred}")
    return pred

Lista de Verificación de Mitigación

🔟 ML10 - Envenenamiento de Modelo

Critical

Descripción General

El envenenamiento de modelo modifica directamente los pesos, parámetros o arquitectura del modelo entrenado para inyectar puertas traseras o alterar el comportamiento. A diferencia del envenenamiento de datos (que corrompe los datos de entrenamiento), el envenenamiento de modelo se dirige al artefacto del modelo en sí, a través de repositorios de modelos comprometidos, amenazas internas o ataques a la cadena de suministro en el pipeline de almacenamiento y despliegue del modelo.

Riesgo

Un modelo directamente envenenado puede contener puertas traseras altamente dirigidas que son virtualmente indetectables mediante pruebas estándar. El atacante tiene control preciso sobre el comportamiento del modelo en entradas de activación. Si el registro de modelos o el pipeline de despliegue está comprometido, cada despliegue usa el modelo envenenado.

Ejemplo de Código Vulnerable

Python ❁EBad
import mlflow

# Loading model from registry with no integrity checks
model_uri = "models:/fraud-detector/Production"
model = mlflow.pyfunc.load_model(model_uri)

# No hash verification
# No signature validation
# No comparison with expected model metrics
# Model registry has weak access controls
# Anyone with push access can replace the model

predictions = model.predict(new_data)

Ejemplo de Código Seguro

Python ✁EGood
import mlflow
import hashlib
from sigstore.verify import Verifier

# Verify model signature before loading
model_uri = "models:/fraud-detector/Production"
model_path = mlflow.artifacts.download_artifacts(model_uri)

# Cryptographic signature verification
verifier = Verifier.production()
verifier.verify(
    model_path,
    expected_identity="ml-team@company.com"
)

# Verify model hash against approved registry
model_hash = hash_directory(model_path)
approved_hash = get_approved_hash("fraud-detector", "Production")
assert model_hash == approved_hash, "Model integrity check failed!"

# Validate model metrics on reference dataset before serving
model = mlflow.pyfunc.load_model(model_path)
ref_score = evaluate(model, reference_dataset)
assert ref_score >= MINIMUM_ACCURACY, "Model quality below threshold"

predictions = model.predict(new_data)

Lista de Verificación de Mitigación

📊 Tabla Resumen

ID Vulnerabilidad Severidad Mitigación Clave
ML01Ataque de Manipulación de EntradaCriticalEntrenamiento adversario, validación de entrada, umbrales de confianza
ML02Ataque de Envenenamiento de DatosCriticalDetección de valores atípicos, procedencia de datos, comparación con línea base
ML03Ataque de Inversión de ModeloHighPrivacidad Diferencial, salida mínima, limitación de tasa
ML04Ataque de Inferencia de PertenenciaHighRegularización, entrenamiento DP, sin exposición de probabilidades
ML05Robo de ModeloCriticalLimitación de tasa, marcas de agua, detección de patrones de consulta
ML06Ataques a la Cadena de Suministro de IACriticalSafeTensors, verificación de hash, solo fuentes confiables
ML07Ataque de Transfer LearningHighModelos base confiables, escaneo de puertas traseras, ajuste fino completo
ML08Sesgo de ModeloMediumMonitoreo de deriva, validación de entrada, reentrenamiento automático
ML09Ataque de Integridad de SalidaHighTLS/mTLS, firma de respuestas, validación de salida
ML10Envenenamiento de ModeloCriticalFirma de modelos, control de acceso a registros, validación de métricas