Qu'est-ce que l'OWASP ML Security Top 10 ?

L'OWASP Machine Learning Security Top 10 identifie les risques de sécurité les plus significatifs spécifiques aux systèmes de machine learning. Contrairement aux logiciels traditionnels, les systèmes ML sont vulnérables à des attaques uniques ciblant les données d'entraînement, les composants internes du modèle et les pipelines d'inférence. Ce guide couvre les attaques adversariales, l'empoisonnement des données, le vol de modèle, les risques de la chaîne d'approvisionnement, et bien plus  Eavec des exemples de code Python pratiques.

1�E�⃣ ML01 - Attaque par Manipulation d'Entrée

Critical

Vue d'ensemble

Les attaques par manipulation d'entrée (attaques adversariales) créent des entrées spécialement conçues pour forcer les modèles ML à faire des prédictions incorrectes. De petites perturbations, souvent imperceptibles, sur des images, du texte ou d'autres entrées peuvent tromper les classificateurs, contourner les systèmes de détection et échapper aux filtres de contenu. Il s'agit du vecteur d'attaque le plus connu spécifique au ML.

Risque

Les exemples adversariaux peuvent contourner des systèmes ML critiques pour la sécurité : perception des véhicules autonomes, détection de malwares, détection de fraude et modération de contenu. Un attaquant peut faire en sorte qu'un panneau stop soit classifié comme un panneau de limitation de vitesse, ou rendre un malware apparemment bénin pour un antivirus basé sur ML.

Exemple de Code Vulnérable

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

Exemple de Code Sécurisé

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}

Liste de Contrôle des Mesures d'Atténuation

2�E�⃣ ML02 - Attaque par Empoisonnement des Données

Critical

Vue d'ensemble

Les attaques par empoisonnement des données injectent des échantillons malveillants dans les ensembles de données d'entraînement pour corrompre le comportement appris du modèle. Les attaquants peuvent introduire des portes dérobées (patterns de déclenchement qui causent des classifications erronées spécifiques), déplacer les frontières de décision ou dégrader la précision globale du modèle. Cela est particulièrement dangereux lorsque les données d'entraînement proviennent d'Internet ou de contenu généré par les utilisateurs.

Risque

Un modèle empoisonné peut se comporter normalement sur des entrées propres mais classifier de manière erronée lorsqu'un pattern de déclenchement spécifique est présent. Par exemple, un classificateur de malware avec porte dérobée pourrait approuver tout échantillon de malware contenant une séquence d'octets spécifique. L'attaque est furtive car la précision du modèle sur les données propres reste élevée.

Exemple de Code Vulnérable

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

Exemple de Code Sécurisé

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)

Liste de Contrôle des Mesures d'Atténuation

3�E�⃣ ML03 - Attaque par Inversion de Modèle

High

Vue d'ensemble

Les attaques par inversion de modèle reconstruisent les données d'entraînement sensibles en interrogeant le modèle et en analysant ses sorties. Un attaquant peut récupérer des informations privées telles que des visages, des dossiers médicaux ou des données personnelles utilisées pendant l'entraînement. Cela est particulièrement préoccupant pour les modèles entraînés sur des ensembles de données sensibles (santé, biométrie, données financières).

Risque

L'inversion de modèle peut violer les réglementations sur la confidentialité des données (RGPD, HIPAA) en exposant des informations personnellement identifiables provenant des données d'entraînement. Un attaquant avec accès à l'API d'un modèle de reconnaissance faciale pourrait reconstruire les visages d'individus dans l'ensemble d'entraînement.

Exemple de Code Vulnérable

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

Exemple de Code Sécurisé

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
    })

Liste de Contrôle des Mesures d'Atténuation

4�E�⃣ ML04 - Attaque par Inférence d'Appartenance

High

Vue d'ensemble

Les attaques par inférence d'appartenance déterminent si un point de données spécifique a été utilisé dans l'ensemble de données d'entraînement du modèle. En analysant les scores de confiance du modèle et son comportement sur des entrées connues vs inconnues, les attaquants peuvent inférer des informations d'appartenance privées. Il s'agit d'une menace significative pour la confidentialité des modèles entraînés sur des données sensibles.

Risque

L'inférence d'appartenance peut révéler que les données d'un individu spécifique ont été utilisées pour l'entraînement  Epar exemple, confirmer que le dossier d'un patient était dans un ensemble de données cliniques, ou que le visage d'une personne a été utilisé pour l'entraînement à la surveillance. Cela viole les attentes de confidentialité et potentiellement des réglementations comme le RGPD.

Exemple de Code Vulnérable

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!

Exemple de Code Sécurisé

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

Liste de Contrôle des Mesures d'Atténuation

5�E�⃣ ML05 - Vol de Modèle

Critical

Vue d'ensemble

Les attaques par vol de modèle (extraction de modèle) créent une copie fonctionnelle d'un modèle ML propriétaire en l'interrogeant systématiquement et en entraînant un modèle de substitution sur les paires entrée-sortie. Le modèle volé peut ensuite être utilisé pour trouver des exemples adversariaux, concurrencer commercialement ou rétro-ingénier les données d'entraînement du modèle.

Risque

Un modèle volé représente une perte de propriété intellectuelle et d'avantage concurrentiel. Le modèle extrait peut être utilisé hors ligne pour créer des attaques adversariales ou pour comprendre les frontières de décision. Des millions de dollars d'investissement en entraînement peuvent être reproduits avec des milliers de requêtes API.

Exemple de Code Vulnérable

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

Exemple de Code Sécurisé

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
    })

Liste de Contrôle des Mesures d'Atténuation

6�E�⃣ ML06 - Attaques de la Chaîne d'Approvisionnement IA

Critical

Vue d'ensemble

Les attaques de la chaîne d'approvisionnement IA ciblent le pipeline de développement ML : modèles pré-entraînés provenant de hubs de modèles, ensembles de données tiers, frameworks ML et dépendances. Les modèles malveillants peuvent contenir des portes dérobées cachées, et les bibliothèques compromises peuvent injecter des vulnérabilités. Les formats de sérialisation utilisés par les frameworks ML (Pickle, SavedModel) peuvent exécuter du code arbitraire au chargement.

Risque

Le chargement d'un fichier de modèle malveillant peut exécuter du code arbitraire (attaques de désérialisation Pickle). Les modèles pré-entraînés provenant de sources non fiables peuvent contenir des portes dérobées. Les bibliothèques ML compromises affectent tous les utilisateurs en aval. La chaîne d'approvisionnement ML a moins de contrôles de sécurité que les chaînes d'approvisionnement logicielles traditionnelles.

Exemple de Code Vulnérable

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

Exemple de Code Sécurisé

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
)

Liste de Contrôle des Mesures d'Atténuation

7�E�⃣ ML07 - Attaque par Apprentissage par Transfert

High

Vue d'ensemble

Les attaques par apprentissage par transfert exploitent la pratique courante de l'affinage de modèles pré-entraînés. Les portes dérobées intégrées dans le modèle de base persistent à travers l'affinage et restent actives dans le modèle en aval. Un attaquant qui publie un modèle pré-entraîné populaire peut compromettre toutes les applications qui l'utilisent comme fondation.

Risque

Les portes dérobées dans les modèles pré-entraînés survivent à l'affinage car elles sont intégrées dans des couches profondes qui sont souvent gelées pendant l'apprentissage par transfert. Un seul modèle de fondation compromis peut affecter des milliers d'applications en aval. L'attaque est évolutive et difficile à détecter.

Exemple de Code Vulnérable

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

Exemple de Code Sécurisé

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)

Liste de Contrôle des Mesures d'Atténuation

8�E�⃣ ML08 - Dérive du Modèle

Medium

Vue d'ensemble

La dérive du modèle se produit lorsque la distribution des données en production diffère significativement de la distribution des données d'entraînement (dérive entraînement-service). Cela peut se produire naturellement au fil du temps (dérive des données) ou être intentionnellement causé par des attaquants qui manipulent la distribution des entrées en production pour dégrader les performances du modèle ou biaiser les prédictions.

Risque

La dérive du modèle provoque des échecs silencieux où le modèle produit des prédictions incorrectes mais confiantes. Dans les systèmes financiers, les attaquants peuvent exploiter la dérive pour contourner la détection de fraude. Dans les systèmes de recommandation, la dérive peut être intentionnellement induite pour promouvoir du contenu ou des produits spécifiques.

Exemple de Code Vulnérable

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

Exemple de Code Sécurisé

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

Liste de Contrôle des Mesures d'Atténuation

9�E�⃣ ML09 - Attaque d'Intégrité de Sortie

High

Vue d'ensemble

Les attaques d'intégrité de sortie altèrent les prédictions du modèle après qu'elles quittent le modèle mais avant qu'elles n'atteignent l'application consommatrice. Cela inclut les attaques de type man-in-the-middle sur les API de prédiction, la manipulation de l'infrastructure de service du modèle et l'altération des prédictions en cache. L'attaque cible le pipeline d'inférence plutôt que le modèle lui-même.

Risque

Les prédictions altérées peuvent causer des décisions incorrectes dans les systèmes en aval : approuver des transactions frauduleuses, poser des diagnostics médicaux erronés ou outrepasser les systèmes de sécurité. Parce que le modèle lui-même n'est pas compromis, la surveillance standard du modèle ne détectera pas l'attaque.

Exemple de Code Vulnérable

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!

Exemple de Code Sécurisé

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

Liste de Contrôle des Mesures d'Atténuation

🔟 ML10 - Empoisonnement de Modèle

Critical

Vue d'ensemble

L'empoisonnement de modèle modifie directement les poids, paramètres ou l'architecture du modèle entraîné pour injecter des portes dérobées ou altérer le comportement. Contrairement à l'empoisonnement des données (qui corrompt les données d'entraînement), l'empoisonnement de modèle cible l'artefact du modèle lui-même  Eà travers des dépôts de modèles compromis, des menaces internes ou des attaques de la chaîne d'approvisionnement sur le stockage et le pipeline de déploiement du modèle.

Risque

Un modèle directement empoisonné peut contenir des portes dérobées hautement ciblées qui sont pratiquement indétectables par les tests standard. L'attaquant a un contrôle précis sur le comportement du modèle sur les entrées de déclenchement. Si le registre de modèles ou le pipeline de déploiement est compromis, chaque déploiement utilise le modèle empoisonné.

Exemple de Code Vulnérable

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)

Exemple de Code Sécurisé

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)

Liste de Contrôle des Mesures d'Atténuation

📊 Tableau Récapitulatif

ID Vulnérabilité Sévérité Atténuation Clé
ML01Attaque par Manipulation d'EntréeCriticalEntraînement adversarial, validation des entrées, seuils de confiance
ML02Attaque par Empoisonnement des DonnéesCriticalDétection d'anomalies, provenance des données, comparaison de référence
ML03Attaque par Inversion de ModèleHighConfidentialité Différentielle, sortie minimale, limitation de débit
ML04Attaque par Inférence d'AppartenanceHighRégularisation, entraînement DP, pas d'exposition des probabilités
ML05Vol de ModèleCriticalLimitation de débit, filigrane, détection de patterns de requêtes
ML06Attaques de la Chaîne d'Approvisionnement IACriticalSafeTensors, vérification de hachage, sources fiables uniquement
ML07Attaque par Apprentissage par TransfertHighModèles de base fiables, scan de portes dérobées, affinage complet
ML08Dérive du ModèleMediumSurveillance de la dérive, validation des entrées, réentraînement automatique
ML09Attaque d'Intégrité de SortieHighTLS/mTLS, signature des réponses, validation de sortie
ML10Empoisonnement de ModèleCriticalSignature de modèle, contrôle d'accès au registre, validation de métriques