Sicherheit auf Typebene: Die Zukunft der sicheren KI-Codegenerierung?
4. Juni 2026
0 Min. LesezeitEinführung
Code wird schneller denn je geschrieben und generiert – leider entstehen dadurch auch Sicherheitslücken immer schneller. Ein LLM darum zu bitten, keine Sicherheitslücken einzubauen, funktioniert nicht immer. Es wird immer deutlicher, dass die heutige Art der Softwareentwicklung – manuell oder mit Unterstützung – nicht ausreicht, um zuverlässig, konsistent und nachweislich sicheren Code zu schreiben.
Der zunehmende Erfolg von Rust hat gezeigt, dass sich ganze Klassen von Schwachstellen auf praktikable und ergonomische Weise vollständig beseitigen lassen. Rust hat praktisch alle (die meisten) speicherbezogenen Korruptionsschwachstellen zur Compile-Zeit beseitigt. Warum sollten wir uns also auf diese Klasse von Schwachstellen beschränken?
In diesem Beitrag zeige ich, wie sich Code so schreiben lässt, dass Sicherheitslücken in Webanwendungen nicht kompilierbar sind (oder die Typprüfung nicht bestehen). Außerdem erläutere ich, wie sicherheitsorientierte Bibliotheken oder gezielt eingesetzte Bibliotheks-Wrapper ganze Klassen von Schwachstellen verhindern können – unabhängig davon, ob der Code von Menschen oder einem LLM geschrieben wird. Anhand von Codebeispielen in Python und Rust zeige ich Muster, mit denen sich Schwachstellenklassen beseitigen lassen.
Warum Typen?
Richtig eingesetzt, können Typsysteme unglaublich leistungsstarke Werkzeuge sein. Sie ermöglichen es, die Invarianten eines Systems festzuschreiben – also alle Regeln, Eigenschaften und Beziehungen sämtlicher Daten in Ihrem Programm. Anstatt einen hilfreichen Kommentar zu hinterlassen oder Laufzeitprüfungen durchzuführen (die womöglich erst greifen, wenn Ihr erster Kunde etwas Wichtiges tun möchte), können Sie sicherstellen, dass jeder Aufruf Ihrer add-Funktion ausnahmslos zwei int-Typen über die gesamte Codebasis hinweg übergibt.
Meiner Erfahrung nach enthält Code, den ich mit sehr strengen Typen schreibe, deutlich weniger Laufzeitfehler als Code ohne diese Typen. Denn ich muss jeden Parameter, alle Daten und jede Eingabe durchdenken. Wenn ich die Anforderungen passend im Code festschreibe, mache ich deutlich weniger Fehler. Dann enthält mein Code nur noch Fehler in der Geschäftslogik und keine Fälle wie „Hoppla, ich habe einer Funktion für die Addition von Ganzzahlen einen String übergeben“.
Viele Sicherheitslücken sind einfach eine bestimmte Art von Fehler. Wenn meine obige Aussage zutrifft, sollten sich viele dieser Fehler mit einem hinreichend flexiblen Typsystem beheben lassen.
Trusted Types
Dieser Beitrag ist unter anderem von der Trusted Types Web API inspiriert. Diese API verhindert die meisten Formen von DOM-XSS, indem sie sicherstellt, dass XSS-Injection-Sinks nur bekannte sichere Werte akzeptieren, beispielsweise Werte, die von einem geeigneten XSS-Sanitizer bereinigt wurden. Mit CSP lässt sich die API zusätzlich einschränken, sodass die Verwendung der Trusted Types API vorgeschrieben ist. Andernfalls wird ein TypeError ausgelöst.
Ein ähnlicher Mechanismus kommt im Linux-Kernel mit dem Makro __user zum Einsatz, das sicherstellt, dass Zeiger aus dem Userspace korrekt verarbeitet werden.
In den folgenden Abschnitten betrachten wir, wie sich diese Technik verallgemeinern lässt und wie Sie sie auf beliebige Klassen von Sicherheitslücken anwenden können.
IDOR beheben
Insecure Direct Object Reference (IDOR) ist eine hartnäckige Sicherheitslücke, die auf fehlende Authentifizierungs- und/oder Autorisierungsprüfungen bei API-Aktionen zurückgeht. Ein klassisches Beispiel ist ein API-Endpunkt, der eine fortlaufende Benutzer-ID entgegennimmt und die zugehörigen Benutzerdaten zurückgibt. Ändert man jedoch den Wert der Benutzer-ID, liefert er ohne entsprechende Autorisierungsprüfungen die Daten eines anderen Benutzers zurück.
Statistisch gesehen verwenden Sie als Leser wahrscheinlich Python. Deshalb untersuchen wir diese Schwachstelle zunächst und zeigen, wie sie sich in Python allein mit Typannotationen beheben lässt.
In Python
Wir beginnen mit einem anfälligen Beispiel:
Das sieht nach einem ganz normalen API-Aufruf aus: Sie übergeben eine ganzzahlige Benutzer-ID, fragen die Datenbank ab und geben das Ergebnis als Antwort zurück. Aber hoppla! Wir haben keinerlei Authentifizierungs- oder Autorisierungsprüfungen eingebaut. Jeder Benutzer kann den Kontostand eines beliebigen anderen Benutzers abrufen, sofern er dessen ID kennt – ein klassischer IDOR-Fall. Wir erhalten den Schwachstellenbericht, zahlen der forschenden Person die Prämie und aktualisieren die Funktion wie folgt:
Perfekt, die Benutzerdaten sind sicher. Doch ein solcher Fehler passiert erschreckend leicht. Eine einzige vergessene Authentifizierungsprüfung an einem API-Endpunkt kann erhebliche Folgen haben. Betrachten wir nun denselben Endpunkt, diesmal mit einem Typsystem, das sicherstellt, dass diese Schwachstelle nicht erneut auftritt:
Der Code ist hier deutlich umfangreicher. Im Gesamtbild geht er jedoch ohnehin in Ihrem Authentifizierungs- und Autorisierungscode auf.
Die wichtigste Änderung im neuen Code: Wir reichen keine einfachen Typen mehr herum (wie zuvor die Benutzer-ID als int). Eingabedaten werden in einer opaken Klasse gekapselt und sind nur zugänglich, wenn Sie nachweisen, dass Authentifizierung und Autorisierung bereits durchgeführt wurden. Da Ihre Klasse UncheckedUserID nichts Nützliches tun kann (etwa in einem SQL-Ausdruck verwendet werden), gibt die Typprüfung zuverlässig einen Fehler aus, wenn Sie vergessen haben, die Authentifizierungsprüfung durchzuführen und den eigentlichen Wert user_id auszulesen.
Da Python dynamisch ist, könnten Sie die Prüfung technisch umgehen und ohne gültiges AuthenticationGuard auf den internen Wert zugreifen. Man kann jedoch hoffen, dass solcher unerwünschter Code bei einer PR-Prüfung auffällt. Andere Sprachen wie Rust bieten deutlich stärkere Garantien: Selbst mit unsauberem Code lässt sich nicht direkt auf den internen Wert der Wrapper-Klasse zugreifen. Natürlich wird der Code weiterhin ausgeführt, sodass extreme Maßnahmen wie das Auslesen des Speichers einer Instanz möglich sind. Wenn Sie aber lediglich sicherstellen möchten, dass Ihre Authentifizierung konsistent ist – warum sollten Sie das tun?
In Rust
Sichtbarkeitsangaben in Rust bieten eine strenge Kontrolle darüber, welcher Code auf den gekapselten user_id-Wert zugreifen kann. Damit lassen sich Authentifizierungs- und Autorisierungsprüfungen noch zuverlässiger sicherstellen. Im folgenden Beispiel verwenden wir Axum mit einem Extractor, der sicherstellt, dass der Eingabewert korrekt in unsere sichere Wrapper-Klasse deserialisiert wird.
Praxistauglichkeit
Diese Methode zur Behebung von Sicherheitslücken setzt natürlich eine erhebliche Unterstützung voraus, insbesondere bei etablierten Softwareprojekten. Es braucht eine Entwicklungsinfrastruktur, die sicherstellt, dass diese Muster konsequent eingehalten werden. Dafür kommen vor allem zwei Ansätze infrage: Wrapper-Bibliotheken oder benutzerdefinierte Linter-Regeln.
Nehmen wir als Beispiel den Python-Fall. Als Unternehmen könnten Sie die Bibliothek MyOrgFastAPI vorschreiben, einen schlanken Wrapper für FastAPI. Damit stellen Sie sicher, dass alle API-Endpunkte benutzerdefinierte Typen verwenden, die diese Sicherheitsanforderungen erfüllen. Kein Endpunkt darf ein Argument vom Typ str entgegennehmen; stattdessen muss es eine Form von UncheckedString sein.
Alternativ können Sie diese Regeln auf Linter-Ebene umsetzen, wenn Sie keine eigene Wrapper-Bibliothek pflegen möchten. Entwickler erhalten dadurch zwar nicht so früh Feedback, doch ein Verbot einfacher Typen bietet weiterhin dasselbe Sicherheitsnetz.
Ganz gleich, welchen Ansatz Sie wählen: So stellen Sie sicher, dass jeder einzelne Fall korrekt behandelt wird. Dieses Codemuster lässt sich ganz natürlich auf den Schutz vor allen möglichen Schwachstellen ausweiten:
Ein
UncheckedStringmuss ordnungsgemäß bereinigt werden, bevor er als einfacher String an den Benutzer zurückgegeben wird. So wird XSS verhindert.Ein
UncheckedStringkann nicht an eine SQL-Abfrage angehängt werden. So werden klassische SQL-Schwachstellen verhindert.Ein
UncheckedStringkann nicht an eine Befehlszeichenfolge angehängt werden. So werden Command-Injection-Schwachstellen verhindert.
Die Liste ließe sich fortsetzen.
Solche Muster eignen sich gleichermaßen für menschliche Entwickler und KI-Agenten. Einen KI-Agenten überall zur Authentifizierung aufzufordern, ist möglicherweise nicht hundertprozentig zuverlässig. Authentifizierung hingegen während der Kompilierung oder beim Linting durchzusetzen, kann äußerst wirkungsvoll sein.
Fazit
Sicherheit auf diese Weise auf Typ-Ebene umzusetzen, kann sowohl für menschliche Entwickler als auch für KI-Agenten ein äußerst wirkungsvolles Mittel sein. Zwar ist insbesondere bei bestehenden Softwareprojekten zunächst ein gewisser Mehraufwand nötig, doch der Nutzen kann erheblich sein: Ganze Klassen von Schwachstellen lassen sich vollständig beseitigen.
Wie Sie mit Ihren Daten umgehen, hängt stark vom jeweiligen Projekt ab. Wenn Sie sich jedoch frühzeitig damit befassen, zahlt sich das für sicheren Code aus.
WHITEPAPER
Die KI-Sicherheitskrise in Ihrer Python-Umgebung
Angesichts des rasanten Entwicklungstempos: Wissen Sie wirklich, worauf Ihre KI-Umgebung zugreifen kann?
