Die 10 kritischsten Sicherheitsrisiken bei Systemen für maschinelles Lernen und wie man sie entschärfen kann.
Die OWASP Machine Learning Security Top 10 identifiziert die wichtigsten Sicherheitsrisiken, die speziell für Machine Learning-Systeme gelten. Im Gegensatz zu herkömmlicher Software sind ML-Systeme anfällig für einzigartige Angriffe, die auf Trainingsdaten, Modellinterna und Inferenzpipelines abzielen. Dieser Leitfaden befasst sich mit feindlichen Angriffen, Data Poisoning, Modelldiebstahl, Risiken in der Lieferkette und mehr - mit praktischen Python-Codebeispielen.
Angriffe zur Manipulation von Eingaben (adversarische Angriffe) nutzen speziell entwickelte Eingaben, um ML-Modelle zu falschen Vorhersagen zu veranlassen. Durch kleine, oft nicht wahrnehmbare Veränderungen von Bildern, Text oder anderen Eingaben können Klassifizierungssysteme getäuscht, Erkennungssysteme umgangen und Inhaltsfilter umgangen werden. Dies ist der bekannteste ML-spezifische Angriffsvektor.
Angreifer können sicherheitskritische ML-Systeme umgehen: Wahrnehmung autonomer Fahrzeuge, Malware-Erkennung, Betrugserkennung und Inhaltsmoderation. Ein Angreifer kann dafür sorgen, dass ein Stoppschild als Geschwindigkeitsbegrenzungsschild eingestuft wird, oder Malware für ein ML-basiertes Antivirenprogramm als harmlos erscheinen lassen.
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 — no 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
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}
Bei Data Poisoning-Angriffen werden bösartige Proben in Trainingsdatensätze injiziert, um das gelernte Verhalten des Modells zu verfälschen. Angreifer können Hintertüren einführen (Auslösemuster, die bestimmte Fehlklassifizierungen verursachen), Entscheidungsgrenzen verschieben oder die Modellgenauigkeit insgesamt verschlechtern. Dies ist besonders gefährlich, wenn die Trainingsdaten aus dem Internet oder von Benutzern erstellten Inhalten stammen.
Ein vergiftetes Modell kann sich bei sauberen Eingaben normal verhalten, aber eine Fehlklassifizierung vornehmen, wenn ein bestimmtes Auslösemuster vorhanden ist. Zum Beispiel könnte ein Malware-Klassifikator, der mit einem Backdoor versehen ist, jedes Malware-Muster, das eine bestimmte Byte-Sequenz enthält, anerkennen. Der Angriff ist unauffällig, da die Modellgenauigkeit bei sauberen Daten hoch bleibt.
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
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 — possible data poisoning") model.fit(X_clean, y_clean)
Bei Modellinversionsangriffen werden sensible Trainingsdaten rekonstruiert, indem das Modell abgefragt und seine Ergebnisse analysiert werden. Ein Angreifer kann private Informationen wie Gesichter, Krankenakten oder persönliche Daten, die beim Training verwendet wurden, wiederherstellen. Dies ist vor allem für Modelle bedenklich, die mit sensiblen Datensätzen trainiert wurden (Gesundheitswesen, Biometrie, Finanzdaten).
Die Modellinversion kann gegen Datenschutzbestimmungen (GDPR, HIPAA) verstoßen, indem sie persönlich identifizierbare Informationen aus den Trainingsdaten offenlegt. Ein Angreifer mit API-Zugriff auf ein Gesichtserkennungsmodell könnte die Gesichter von Personen im Trainingssatz rekonstruieren.
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 — enables 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
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 — no probability vector prediction = int(np.argmax(result)) log_query(request.remote_addr, prediction) # Audit logging return jsonify({ "prediction": prediction # No probabilities, no confidence scores })
Angriffe auf die Zugehörigkeit ermitteln, ob ein bestimmter Datenpunkt im Trainingsdatensatz des Modells verwendet wurde. Durch die Analyse der Konfidenzwerte und des Verhaltens des Modells bei bekannten und unbekannten Eingaben können Angreifer auf private Mitgliedsinformationen schließen. Dies stellt eine erhebliche Bedrohung für die Privatsphäre von Modellen dar, die mit sensiblen Daten trainiert wurden.
Der Rückschluss auf die Zugehörigkeit kann zeigen, dass die Daten einer bestimmten Person für das Training verwendet wurden - zum Beispiel, um zu bestätigen, dass der Datensatz eines Patienten in einem klinischen Datensatz war oder dass das Gesicht einer Person für das Überwachungstraining verwendet wurde. Dies verstößt gegen die Datenschutzerwartungen und möglicherweise gegen Vorschriften wie die Datenschutz-Grundverordnung.
from sklearn.neural_network import MLPClassifier # Overfitted model — memorizes 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 → membership 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!
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
Beim Modelldiebstahl (Modellextraktion) wird eine funktionale Kopie eines geschützten ML-Modells erstellt, indem es systematisch abgefragt und ein Ersatzmodell anhand der Eingabe-Ausgabe-Paare trainiert wird. Das gestohlene Modell kann dann verwendet werden, um gegnerische Beispiele zu finden, kommerziell zu konkurrieren oder die Trainingsdaten des Modells zurückzuentwickeln.
Ein gestohlenes Modell bedeutet den Verlust von geistigem Eigentum und Wettbewerbsvorteilen. Das extrahierte Modell kann offline verwendet werden, um gegnerische Angriffe zu entwickeln oder um Entscheidungsgrenzen zu verstehen. Trainingsinvestitionen in Millionenhöhe können mit Tausenden von API-Abfragen repliziert werden.
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 — unlimited queries # No anomaly detection on query patterns # Attacker can extract model with ~10K queries
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 })
Angriffe auf die KI-Lieferkette zielen auf die ML-Entwicklungspipeline ab: vortrainierte Modelle von Modell-Hubs, Datensätze von Drittanbietern, ML-Frameworks und Abhängigkeiten. Bösartige Modelle können versteckte Hintertüren enthalten, und kompromittierte Bibliotheken können Schwachstellen einschleusen. Die von ML-Frameworks verwendeten Serialisierungsformate (Pickle, SavedModel) können beim Laden beliebigen Code ausführen.
Durch das Laden einer bösartigen Modelldatei kann beliebiger Code ausgeführt werden (Pickle-Deserialisierungsangriffe). Vortrainierte Modelle aus nicht vertrauenswürdigen Quellen können Hintertüren enthalten. Kompromittierte ML-Bibliotheken wirken sich auf alle nachgeschalteten Benutzer aus. In der ML-Lieferkette gibt es weniger Sicherheitskontrollen als in herkömmlichen Software-Lieferketten.
import pickle import torch # Loading untrusted model — arbitrary 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
import torch import hashlib from safetensors.torch import load_file # Use SafeTensors — no 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 )
Bei Angriffen durch Transferlernen wird die gängige Praxis der Feinabstimmung von vorab trainierten Modellen ausgenutzt. In das Basismodell eingebettete Hintertüren bleiben während der Feinabstimmung bestehen und sind im nachgelagerten Modell weiterhin aktiv. Ein Angreifer, der ein beliebtes vorab trainiertes Modell veröffentlicht, kann alle Anwendungen gefährden, die es als Grundlage verwenden.
Hintertüren in vortrainierten Modellen überleben die Feinabstimmung, da sie in tiefen Schichten eingebettet sind, die während des Transfer-Learnings oft eingefroren werden. Ein einziges kompromittiertes Basismodell kann Tausende von nachgelagerten Anwendungen beeinträchtigen. Der Angriff ist skalierbar und schwer zu erkennen.
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 — preserves 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
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)
Modellverzerrungen treten auf, wenn die Datenverteilung in der Produktion erheblich von der Verteilung der Trainingsdaten abweicht (trainingsbedingte Verzerrung). Dies kann im Laufe der Zeit auf natürliche Weise geschehen (Datendrift) oder von Angreifern absichtlich herbeigeführt werden, die die Verteilung der Eingabedaten in der Produktion manipulieren, um die Modellleistung zu beeinträchtigen oder die Vorhersagen zu verfälschen.
Modellverzerrungen führen zu stillen Fehlern, bei denen das Modell falsche, aber zuverlässige Vorhersagen macht. In Finanzsystemen können Angreifer die Schiefe ausnutzen, um die Betrugserkennung zu umgehen. In Empfehlungssystemen können Verzerrungen absichtlich herbeigeführt werden, um bestimmte Inhalte oder Produkte zu fördern.
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
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 → trigger retraining return prediction
Angriffe auf die Ausgabeintegrität manipulieren die Modellvorhersagen, nachdem sie das Modell verlassen haben, aber bevor sie die nutzende Anwendung erreichen. Dazu gehören Man-in-the-Middle-Angriffe auf Vorhersage-APIs, Manipulationen der Infrastruktur für die Modellbereitstellung und Manipulationen an zwischengespeicherten Vorhersagen. Der Angriff zielt eher auf die Inferenzpipeline als auf das Modell selbst.
Manipulierte Vorhersagen können zu falschen Entscheidungen in nachgelagerten Systemen führen: Genehmigung betrügerischer Transaktionen, Fehldiagnosen von medizinischen Zuständen oder Außerkraftsetzen von Sicherheitssystemen. Da das Modell selbst nicht kompromittiert ist, wird die Standardmodellüberwachung den Angriff nicht erkennen.
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!
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
Beim Model Poisoning werden die Gewichte, Parameter oder die Architektur des trainierten Modells direkt verändert, um Hintertüren einzuschleusen oder das Verhalten zu ändern. Im Gegensatz zum Data Poisoning (bei dem Trainingsdaten beschädigt werden) zielt Model Poisoning auf das Modell-Artefakt selbst ab - durch kompromittierte Modell-Repositories, Insider-Bedrohungen oder Supply-Chain-Angriffe auf die Modellspeicher- und -bereitstellungspipeline.
Ein direkt vergiftetes Modell kann sehr gezielte Hintertüren enthalten, die durch Standardtests praktisch nicht aufzuspüren sind. Der Angreifer hat genaue Kontrolle über das Verhalten des Modells bei Auslösereingaben. Wenn die Modellregistrierung oder die Bereitstellungspipeline kompromittiert wird, verwendet jede Bereitstellung das vergiftete Modell.
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)
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)
| ID | Schwachstelle | Schweregrad | Schlüssel Abschwächung |
|---|---|---|---|
| ML01 | Angriff zur Manipulation der Eingabe | Critical | Adversariales Training, Eingabevalidierung, Vertrauensschwellen |
| ML02 | Datenvergiftungsangriff | Critical | Ausreißererkennung, Datenherkunft, Baseline-Vergleich |
| ML03 | Modell Inversionsangriff | High | Differenzieller Datenschutz, minimale Leistung, Ratenbegrenzung |
| ML04 | Angriff auf Mitgliedschaftsinferenz | High | Regularisierung, DP-Training, keine Wahrscheinlichkeitsexposition |
| ML05 | Modell Diebstahl | Critical | Ratenbegrenzung, Wasserzeichen, Abfragemustererkennung |
| ML06 | AI-Angriffe auf die Lieferkette | Critical | SafeTensors, Hash-Verifizierung, nur vertrauenswürdige Quellen |
| ML07 | Transfer-Learning-Angriff | High | Vertrauenswürdige Basismodelle, Backdoor-Scanning, vollständige Feinabstimmung |
| ML08 | Modell Schräglage | Medium | Driftüberwachung, Eingabevalidierung, automatische Nachschulung |
| ML09 | Angriff auf die Integrität der Ausgabe | High | TLS/mTLS, Antwortsignierung, Validierung der Ausgabe |
| ML10 | Modell Vergiftung | Critical | Modellsignierung, Zugangskontrolle zu Registern, Validierung von Metriken |