TL;DR: Die Eingabe Ihrer API ist eine Angriffsfläche, also testen Sie sie auch als solche. Schreiben Sie Negativfälle, die übergroße Felder, falsche Typen, fehlerhafte Bodies und Injektionsstrings senden, und stellen Sie dann sicher, dass der Endpunkt einen 4xx und niemals einen 5xx zurückgibt. Machen Sie die Schema-Validierung zu einer Sicherheitskontrolle mit `additionalProperties: false`, Enums und Längenbeschränkungen. Führen Sie die gesamte Suite bei jeder Änderung im CI aus. KI-Agenten machen dies dringlich: Sie generieren und leiten Payloads mit Maschinengeschwindigkeit weiter, so dass „diese Daten laden“ leise zu „diesen Code ausführen“ wird und nun skaliert.
Die meisten Testsuiten beweisen, dass Ihre API funktioniert, wenn der Aufrufer höflich ist. Sie senden einen gültigen Body, erhalten einen 200er, die Assertion ist erfolgreich. Dieses Ergebnis sagt Ihnen fast nichts darüber, was passiert, wenn der Body feindselig ist. Unvertrauenswürdige Eingaben sind alle Daten, die Ihr Endpunkt nicht selbst generiert hat: Request Bodies, Query Strings, Header, Datei-Uploads, Webhook-Payloads und der JSON, den ein KI-Agent ad hoc zusammenstellt. All dies verdient die gleiche Annahme, nämlich dass jemand irgendwann die schlechteste mögliche Version davon senden wird.
Im Juli 2026 beschrieb Hugging Face einen Sicherheitsvorfall, dessen Angriffsvektor Daten waren, nicht ein gestohlenes Passwort. Wir haben die Lehren aus dieser Verletzung gesondert behandelt; dieser Leitfaden ist der praktische Teil. Sie werden Tests erstellen, die die Art von Eingaben senden, die ein Angreifer senden würde, und diese dann bei jeder Änderung automatisch ausführen. Die Kategorien stimmen mit den OWASP API Security Top 10 überein, die es sich lohnt, in einem Tab offen zu halten. Apidog ist eine Möglichkeit, den Vertrag zu entwerfen und diese Tests zu steuern, aber die Ideen gelten in jedem Framework, das Sie bereits verwenden.
Eingabe ist eine Angriffsfläche, kein Formularfeld
Validierung wird oft als Höflichkeit der Benutzerfreundlichkeit behandelt: die leere E-Mail abfangen, einen roten Rahmen anzeigen, weitermachen. Diese Betrachtungsweise ist das Problem. Jedes Feld, das Ihre API akzeptiert, ist ein Versprechen, das der Aufrufer brechen kann, und jedes gebrochene Versprechen ist ein Weg in Ihre Logik. Ein limit-Parameter, von dem Sie einen kleinen Integer erwartet haben, wird zu 999999999. Ein filename, von dem Sie ein einzelnes Wort erwartet haben, wird zu ../../etc/passwd. Ein config-Objekt, das Einstellungen enthalten sollte, wird zu einer Reihe von Anweisungen.
Sicherheitstests sind keine separate Disziplin, die am Ende angeflanscht wird. Es ist dieselbe negative Testmethode, die Sie bereits kennen, die auf die Felder abzielt, die Ihnen am wahrscheinlichsten schaden werden. Wenn Sie sich angewöhnen, zu fragen „Was ist das Schlimmste, das in dieses Feld passt?“, sind Sie auf dem besten Weg zu den Praktiken in unserem Leitfaden zu den API-Sicherheits-Best-Practices. Der Rest dieses Artikels wandelt diese eine Frage in konkrete Tests um, die Sie ausführen können.
Wie aus „diese Daten laden“ „diesen Code ausführen“ wurde
Der Vorfall bei Hugging Face ist ein klares Beispiel dafür, warum Eingaben diese Aufmerksamkeit verdienen. Hugging Face erklärte, der Angriffsvektor seien bösartige Datensätze gewesen: ein manipulierter Datensatz löste einen Remote-Code-Datensatzlader aus, und eine Template-Injektion befand sich innerhalb einer Datensatzkonfiguration. Den eigenen Bericht des Unternehmens finden Sie in seinem Sicherheitsvorfallbericht.
Betrachten Sie die Art dieses Fehlers. Ein Endpunkt akzeptierte etwas, das als Daten beschrieben wurde. Das Laden dieser Daten führte zu einem Codepfad, der vom Angreifer gesteuerte Anweisungen ausführen konnte. „Diese Daten laden“ wurde zu „diesen Code ausführen“. Die Template-Injektion ist dieselbe Geschichte in kleinerem Maßstab: Ein Konfigurationswert, der ein inerter Text sein sollte, wurde evaluiert, sodass Text zur Ausführung wurde.
Die Erkenntnis ist nicht, „Hugging Face hat einen seltenen Fehler gemacht.“ Es ist vielmehr, dass jeder Endpunkt, der einen Loader-Namen, ein Format, ein Template, ein serialisiertes Objekt oder einen Konfigurations-Blob akzeptiert, Anweisungen akzeptiert, ob Sie dies beabsichtigt haben oder nicht. Wenn Sie nie einen Test geschrieben haben, der eine feindliche Konfiguration an diesen Endpunkt sendet, haben Sie die Annahme, dass sie inert bleibt, nie tatsächlich überprüft. Diese ungetestete Annahme ist die gesamte Schwachstelle.
Schema-Validierung als Sicherheitskontrolle
Die günstigste Kontrolle, die Sie hinzufügen können, ist ein striktes Schema am Rande. Ein Schema ist nicht nur Dokumentation. Wenn Sie alles ablehnen, was nicht übereinstimmt, wird das Schema zu einem Filter, der läuft, bevor Ihre Geschäftslogik die Anfrage überhaupt sieht. JSON Schema bietet Ihnen die Grundlagen, um diesen Filter eng zu gestalten.
Hier ist ein Schema für die Dataset-Konfiguration aus der Geschichte, so geschrieben, dass die meisten feindseligen Eingaben niemals den Anwendungscode erreichen:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"required": ["loader", "name"],
"properties": {
"loader": { "enum": ["csv", "json", "parquet"] },
"name": { "type": "string", "maxLength": 128, "pattern": "^[\\w .-]+$" },
"rows": { "type": "integer", "minimum": 0, "maximum": 1000000 }
}
}
Lesen Sie es als vier separate Abwehrmaßnahmen. `additionalProperties: false` lehnt ein eingeschmuggeltes `template`-Feld direkt ab, sodass ein Angreifer keines hinzufügen kann. Das `loader`-Enum bedeutet, dass `pickle://` oder jeder Remote-Code-Loader einfach kein gültiger Wert ist. `maxLength` unterbindet den Multi-Megabyte-String, der darauf abzielt, den Speicher zu erschöpfen. Das `pattern` für `name` lehnt die Zeichen `{{` und `'; DROP TABLE` ab, bevor sie weiter gelangen. Keine dieser Zeilen weiß etwas über Angreifer. Sie akzeptieren nur den engen Satz von Eingaben, die Sie tatsächlich unterstützen, und diese Beschränkung ist die Sicherheitseigenschaft.
Eine solche Vertragsvalidierung fängt nicht jeden Exploit ab, und kein Schema wird dies tun. Was sie jedoch schließt, ist eine spezifische und häufige Kategorie: der „Wir haben nie geprüft, was dieser Endpunkt akzeptiert“-Fehler. In dieser Kategorie beginnen überraschend viele Sicherheitsverletzungen.
Negativtests: Beweisen, dass der Endpunkt „Nein“ sagt
Happy-Path-Tests stellen sicher, dass gute Eingaben gute Ausgaben erzeugen. Negative Tests stellen sicher, dass schlechte Eingaben eine kontrollierte Ablehnung erzeugen. Der Unterschied ist wichtig, denn eine Ablehnung ist ein Feature: Ein 400er mit einem klaren Fehler bedeutet, dass Ihre API ihre Grenze verteidigt. Ein 500er bedeutet, dass Ihre API die Kontrolle darüber verliert.
Erstellen Sie Negativfälle immer auf dieselbe Weise. Schreiben Sie für jedes Feld auf, was es ablehnen muss: falscher Typ, fehlend wenn erforderlich, vorhanden wenn verboten, zu lang, außerhalb des Bereichs und die Injektionsstrings, die zu seinem Format passen. Stellen Sie dann zwei Dinge an der Antwort sicher. Erstens ist der Status ein 4xx, normalerweise 400 oder 422. Zweitens ist der Status niemals ein 5xx. Ein 500er bedeutet, dass Ihre feindselige Eingabe Code erreicht hat, der nicht darauf vorbereitet war, was genau die Erreichbarkeit ist, die ein Angreifer wünscht. Unsere API-Sicherheitstest-Checkliste enthält eine feldspezifische Startliste, die Sie anpassen können.
Eine Regel hält dies ehrlich: Überprüfen Sie das Verhalten, nicht den Fehlertext. Wenn Sie darauf bestehen, dass die Meldung „ungültiger Loader“ lautet, bricht ein harmloser Refaktor Ihren Test und lehrt das Team, ihn zu lockern. Überprüfen Sie den Statuscode und, wo möglich, stellen Sie sicher, dass überhaupt keine Nebenwirkung eingetreten ist.
Die Injektionsklassen, die einen dedizierten Test wert sind
Einige Injektionsfamilien treten oft genug auf, dass jede dedizierte Testfälle verdient, nicht nur eine einmalige manuelle Überprüfung. Sie müssen hier nicht erschöpfend sein. Sie benötigen einen Prüffall pro Klasse, damit eine Regression lautstark fehlschlägt. Tools, die automatisierte API-Schwachstellen-Erkennung durchführen, können die Abdeckung später erweitern, aber eine Handvoll handgeschriebener Fälle fängt die offensichtlichen Lücken zuerst ab.
- SQL-Injektion: Senden Sie
1); DROP TABLE datasets;--in jedes Feld, das eine Abfrage erreicht. Der Endpunkt sollte dies als Literalwert behandeln und 400 antworten oder ein leeres Ergebnis zurückgeben und niemals einen Datenbankfehler anzeigen. - Template-Injektion: Senden Sie
{{ 7*7 }}und{{ config.__class__ }}in Namens- und Beschriftungsfelder. Wenn die Antwort jemals49enthält, hat eine Template-Engine Ihre Eingabe evaluiert, und das ist ein Remote-Code, der darauf wartet, ausgeführt zu werden. - Unsichere Deserialisierung und Remote-Code-Lader: Senden Sie einen Loader von
pickle://oder ein serialisiertes Objekt, wo ein einfacher Wert hingehört. Dies ist genau das Hugging Face-Muster. Der Endpunkt sollte unbekannte Loader per Whitelist ablehnen und nicht versuchen, hilfreich zu sein. - Befehlsinjektion: Senden Sie
; idund$(id)in jedes Feld, das ein Shell-Argument werden könnte, wie z.B. ein Dateiname oder eine Konvertierungsoption. Ein 200er, der eine Benutzer-ID preisgibt, ist ein kritisches Ergebnis, keine Kuriosität.
Übergroß, fehlerhaft und Content-Type-Verwirrung
Nicht jede feindselige Eingabe ist ein cleverer String. Manche sind einfach zu groß oder falsch geformt, und diese brechen oft Parser, bevor Ihre Validierungslogik überhaupt läuft.
Senden Sie eine übergroße Payload: ein einzelnes Feld, das fünf Megabyte eines Zeichens enthält, oder ein JSON-Array mit einer Million Elementen. Eine gesunde API erzwingt eine Body-Größenbeschränkung und gibt 413 zurück, anstatt Speicher zuzuweisen, bis sie zusammenbricht. Senden Sie auch fehlerhafte Bodies: abgeschnittenes JSON, ein nachgestelltes Komma oder JSON, das tausend Ebenen tief verschachtelt ist, um eine Stapelüberlaufprüfung durchzuführen. Die richtige Antwort ist ein schneller 400er, kein hängender Worker.
Content-Type-Verwirrung ist die Stille. Deklarieren Sie Content-Type: application/json, senden Sie aber XML, oder deklarieren Sie application/xml und senden Sie eine Payload mit einer externen Entität, um auf XXE zu prüfen. Drehen Sie es um und senden Sie JSON als text/plain, um zu sehen, ob ein nachlässiger Parser es trotzdem akzeptiert. Jede Nichtübereinstimmung testet, ob Ihr Server den Header, den Body oder die Übereinstimmung der beiden prüft. Es sollte eine Übereinstimmung erfordern, bevor es etwas parst.
Warum KI-Agenten die Einsätze erhöhen
Alles oben Genannte galt, bevor Agenten existierten. Agenten ändern das Volumen und die Geschwindigkeit. Ein menschlicher Angreifer tippt eine feindliche Anfrage nach der anderen. Ein KI-Agent generiert und leitet Payloads mit Maschinengeschwindigkeit weiter, und er wird mit Freude Eingaben konstruieren, die ein Mensch niemals versuchen würde.
Drei Eigenschaften verschlimmern dies. Agenten synthetisieren Eingaben, sodass sie Feldwerte erzeugen, die kein Mensch geschrieben und kein Test antizipiert hat. Agenten wiederholen und verketten Aufrufe, sodass ein einziges vergiftetes Upstream-Dokument in Sekundenschnelle zu Tausenden von feindlichen Anfragen an Ihren Endpunkt werden kann. Und Agenten leiten Daten weiter, denen sie vertrauen sollten, wodurch eine in einem Datensatz oder einem Webhook versteckte Payload zu einer echten Anfrage an Ihre API wird. Das Hugging Face-Muster, bei dem „diese Daten laden“ zu „diesen Code ausführen“ wird, ist genau die Art von Anweisung, die ein Agent unbemerkt über eine Vertrauensgrenze hinweg ausführen wird. Unser Hinweis zur Prompt-Injection für API-Teams geht auf diese Übergabe tiefer ein. Die Verteidigung ändert sich nicht; sie muss nur automatisch sein, da man den Agentenverkehr nicht manuell überprüfen kann.
Erstellen Sie die Negativsuite und führen Sie sie bei jeder Änderung im CI aus
Verwandeln Sie die obigen Fälle in eine Suite, die bei jedem Pull Request läuft. Hier ist eine kompakte parametrisierte Version in pytest, die einen Staging-Endpunkt anspricht und eine kontrollierte Ablehnung zusichert:
import httpx
import pytest
BASE = "https://staging.internal/v1"
HOSTILE_CONFIGS = [
{"loader": "pickle://s3/models/payload.pkl", "format": "auto"}, # remote-code loader
{"loader": "csv", "name": "{{ 7*7 }}"}, # template injection
{"loader": "csv", "name": "{{ config.__class__ }}"}, # object traversal
{"loader": "csv", "filter": "1); DROP TABLE datasets;--"}, # SQL injection
{"loader": "csv", "name": "A" * 5_000_000}, # oversized field
]
@pytest.mark.parametrize("config", HOSTILE_CONFIGS)
def test_dataset_config_is_refused(config):
r = httpx.post(f"{BASE}/datasets", json={"config": config}, timeout=10)
assert r.status_code in (400, 413, 422), r.text # a boundary that says no
assert r.status_code < 500, "5xx means the payload reached logic it should not"
assert "49" not in r.text, "template rendered: server-side template injection"
Verbinden Sie es mit CI, damit es Merges steuert. Ein minimaler GitHub Actions Job erledigt die Aufgabe:
name: api-abuse-tests
on: [push, pull_request]
jobs:
negative-input:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pytest tests/negative_input.py -q
Hier verdient ein Schema-First-Tool seinen Platz. In Apidog entwerfen Sie den Endpunkt anhand eines OpenAPI-Vertrags, sodass jede Anfrage und Antwort während des Tests gegen diesen Vertrag geprüft wird. Sie können negative Szenarien direkt neben den Happy-Path-Szenarien speichern: übergroße Felder, falsche Typen und die oben genannten Injektionsstrings, jeweils mit einer Zusicherung, dass der Status ein 4xx ist. Dann führen Sie dieselben Szenarien im CI über die Apidog CLI aus, sodass eine Änderung, die die Validierung leise lockert, den Build fehlschlagen lässt, anstatt ausgeliefert zu werden. Wenn Sie es ausprobieren möchten, laden Sie Apidog herunter und fügen Sie ein negatives Szenario zu einem Endpunkt hinzu, den Sie bereits haben.
Seien Sie sich der Grenze bewusst. Apidog ist ein Design-, Test-, Mock- und Dokumentationstool. Es betreibt keine Web Application Firewall, filtert keinen Live-Verkehr oder ersetzt ein SIEM, und die Vertragsvalidierung während des Tests fängt nicht jeden Exploit ab. Was es gut macht, ist, den Vertrag explizit zu machen und Sie ehrlich darüber zu halten, was ein Endpunkt akzeptiert, so dass die Kategorie „Wir haben nie geprüft“ nicht mehr das ist, was Sie in der Produktion überrascht.
Häufig gestellte Fragen
- Was ist der Unterschied zwischen Negativtests und Fuzzing? Negativtests senden einen kuratierten Satz von schlechten Eingaben, die Sie absichtlich ausgewählt haben, einen pro Fehler, der Ihnen wichtig ist. Fuzzing sendet große Mengen zufälliger oder mutierter Eingaben, um Fälle zu finden, an die Sie nicht gedacht haben. Beginnen Sie mit Negativtests, da diese schnell, deterministisch und einfach im CI auszuführen sind. Fuzzing fügen Sie hinzu, wenn Sie eine Breite jenseits Ihrer eigenen Vorstellungskraft wünschen.
- Sollten diese Tests gegen die Produktion laufen? Nein. Führen Sie sie gegen Staging oder eine isolierte Umgebung aus. Einige Fälle, wie die übergroße Payload oder eine Befehlsinjektionssonde, sind darauf ausgelegt, das System zu belasten, und einige könnten Daten mutieren, wenn ein Fehler vorliegt. Eine dedizierte Testumgebung ermöglicht es den Tests, aggressiv zu sein, ohne ein Risiko für echte Benutzer darzustellen.
- Fängt das nicht sowieso eine Firewall oder WAF ab? Eine WAF ist eine nützliche Tiefenverteidigung, aber sie ist kein Ersatz dafür, dass die Anwendung schlechte Eingaben ablehnt. Regeln können umgangen werden, und eine WAF kann Ihre Geschäftslogik nicht kennen. Der Sinn dieser Tests ist es, zu beweisen, dass der Endpunkt selbst „Nein“ sagt, sodass Sie sich nicht auf einen Filter verlassen, den Sie nicht vollständig kontrollieren.
- Wie viele Negativfälle sind pro Endpunkt ausreichend? Zielen Sie auf einen Fall pro Feld pro Fehlerklasse ab, die auftreten kann: falscher Typ, außerhalb des Bereichs, zu lang, verbotenes Feld und jeder Injektionsstring, der zu seinem Format passt. Das sind normalerweise eine Handvoll Fälle pro Endpunkt, nicht Hunderte. Die Abdeckung der Klassen ist wichtiger als die reine Anzahl.
- Stoppt die Schema-Validierung Injektionen vollständig? Nein, und sie sollte nicht Ihre einzige Ebene sein. Ein striktes Schema entfernt einen Großteil fehlerhafter und übergroßer Eingaben und blockiert unerwartete Felder, aber ein Wert kann schema-gültig sein und trotzdem eine SQL- oder Template-Injektion darstellen. Behalten Sie parametrisierte Abfragen, sichere Deserialisierung und Ausgabe-Encoding bei und verwenden Sie das Schema, um die Angriffsfläche zu verkleinern, die diese Schichten verteidigen müssen.
