paint-brush
Analyse häufiger Schwachstellen, die durch Code-generative KI entstehenvon@natanjfrog
4,755 Lesungen
4,755 Lesungen

Analyse häufiger Schwachstellen, die durch Code-generative KI entstehen

von Natan Nehorai11m2024/02/04
Read on Terminal Reader

Zu lang; Lesen

Automatisch generiertem Code kann nicht blind vertraut werden und erfordert dennoch eine Sicherheitsüberprüfung, um die Entstehung von Software-Schwachstellen zu vermeiden.
featured image - Analyse häufiger Schwachstellen, die durch Code-generative KI entstehen
Natan Nehorai HackerNoon profile picture
0-item
1-item

Tools für künstliche Intelligenz wie Bard, ChatGPT und Bing Chat sind derzeit die großen Namen in der auf dem Vormarsch befindlichen Kategorie des Large Language Model (LLM) .


LLMs werden anhand umfangreicher Datensätze trainiert, um mithilfe alltäglicher menschlicher Sprache als Chat-Eingabeaufforderung kommunizieren zu können. Angesichts der Flexibilität und des Potenzials von LLMs integrieren Unternehmen sie in viele Arbeitsabläufe in der Technologiebranche, um unser Leben besser und einfacher zu machen. Eine der wichtigsten KI-Workflow-Integrationen ist ein Plugin zur automatischen Vervollständigung von Code, das die Möglichkeit bietet, passende Codemuster vorherzusehen und zu generieren, um schneller und effizienter zu programmieren.


Auf dem aktuellen Markt für KI-Codegenerator-Tools gibt es viele Akteure, darunter GitHub Copilot , Amazon CodeWhisperer , Google Cloud Code (Duet AI) , Blackbox AI Code Generation und mehr. In diesem Blogbeitrag werden Beispiele für häufige Sicherheitslücken beschrieben, mit denen Entwickler bei der Verwendung solcher Tools konfrontiert sind.


Das Ziel dieses Artikels besteht darin , das Bewusstsein zu schärfen und zu betonen, dass automatisch generiertem Code nicht blind vertraut werden kann und dennoch eine Sicherheitsüberprüfung erforderlich ist, um die Einführung von Software-Schwachstellen zu vermeiden.


Hinweis: Einige Anbieter weisen darauf hin, dass ihre Tools zur automatischen Vervollständigung möglicherweise unsichere Codefragmente enthalten.


In vielen Artikeln wurde bereits betont, dass Autovervollständigungstools bekannte Schwachstellen wie IDOR , SQL-Injection und XSS erzeugen. In diesem Beitrag werden andere Schwachstellentypen hervorgehoben, die stärker vom Kontext des Codes abhängen und bei denen eine hohe Wahrscheinlichkeit besteht, dass durch die automatische KI-Vervollständigung Fehler in Ihren Code eingeschleust werden.


Sicherheits- und KI-Codegenerierungstools Für Code trainierte KI-Modelle werden normalerweise auf großen Codebasen trainiert, mit der Hauptaufgabe, funktionierenden Code zu produzieren.


Für viele Sicherheitsprobleme gibt es eine bekannte und definierte Möglichkeit zur Abhilfe, ohne die Leistung zu beeinträchtigen (z. B. könnten SQL-Injections vermieden werden, indem Benutzereingaben/-parameter nicht direkt in die Abfrage verkettet werden) – und können daher beseitigt werden, da es keinen Grund gibt, das zu schreiben Code auf unsichere Weise verwenden.


Viele Sicherheitsprobleme sind jedoch kontextabhängig und könnten bei der Implementierung als interne Funktionalität vollkommen sicher sein, während sie bei externen Eingaben eines Clients zu einer Schwachstelle werden und daher anhand des Lerndatensatzes der KI schwer zu unterscheiden sind.


Bei solchen Schwachstellen hängt die spezifische Verwendung des Codes normalerweise von anderen Teilen des Codes und dem Gesamtzweck der Anwendung ab.


Hier sind einige häufige Anwendungsfälle und Beispiele, die wir getestet haben und die einige dieser Arten von Schwachstellen aufzeigen:

  1. Anwendungsfall: Abrufen von Dateien aus Benutzereingaben – beliebiges Lesen von Dateien
  2. Anwendungsfall: Vergleich geheimer Token – Typ Jonglieren/Zwang
  3. Anwendungsfall: Mechanismus „Passwort vergessen“ – Unicode-Case-Mapping-Kollisionen
  4. Anwendungsfall: Generieren einer Konfigurationsdatei – Schlechte Sicherheitspraktiken
  5. Anwendungsfall: Konfigurationsobjekte – Inkonsistenzen führen zu unsicherer Deserialisierung


Nachdem wir gesehen hatten, wie die KI-Codegenerierungstools mit den oben genannten Fällen umgehen, haben wir versucht, ihre Fähigkeit zu testen, geeignete Sicherheitsfilter und andere Schadensbegrenzungsmechanismen zu erstellen.


Hier sind einige häufige Anwendungsfälle und Beispiele, die wir getestet haben, indem wir bewusst sicheren Code angefordert haben:

  1. Anwendungsfall „Secure Code Request“: Dateien lesen – Verzeichnisdurchquerung vermeiden
  2. Sichere Code-Anfrage – Datei-Upload – Liste der eingeschränkten Erweiterungen


  1. Anwendungsfall: Abrufen von Dateien aus Benutzereingaben – beliebiges Lesen von Dateien

Viele Anwendungen enthalten Code, der eine Datei basierend auf einem Dateinamen an den Benutzer zurückruft. Die Verwendung von Directory Traversal zum Lesen beliebiger Dateien auf dem Server ist eine gängige Methode von Kriminellen, um an nicht autorisierte Informationen zu gelangen.


Es mag naheliegend erscheinen, vom Benutzer eingegebene Dateinamen/Dateipfade vor dem Zurückholen der Datei zu bereinigen, aber wir gehen davon aus, dass dies für ein KI-Modell, das auf generischen Codebasen trainiert wurde, eine schwierige Aufgabe ist – das liegt daran, dass dies bei einigen Datensätzen nicht erforderlich ist Verwenden Sie bereinigte Pfade als Teil ihrer Funktionalität (dies kann sogar die Leistung beeinträchtigen oder die Funktionalität beeinträchtigen) oder werden durch nicht bereinigte Pfade nicht geschädigt, da sie nur für den internen Gebrauch bestimmt sind.


Vorschlag für ein AI-Tool (Google Cloud Code – Duet AI): Versuchen wir, mithilfe der automatischen Vervollständigung von Google Cloud Code – Duet AI eine grundlegende Dateiabruffunktion aus Benutzereingaben zu generieren. Wir lassen Duet AI unseren Code basierend auf unseren Funktions- und Routennamen automatisch vervollständigen –



Wie wir in Grau sehen können, besteht der Vorschlag zur automatischen Vervollständigung darin, die „ send_file “-Funktion von Flask zu verwenden, bei der es sich um die grundlegende Dateiverwaltungsfunktion von Flask handelt.


Aber selbst in der Dokumentation von Flask heißt es: „ Übergeben Sie niemals von einem Benutzer bereitgestellte Dateipfade “, was bedeutet, dass der Benutzer beim direkten Einfügen von Benutzereingaben in die Funktion „send_file“ die Möglichkeit hat, jede Datei im Dateisystem zu lesen, beispielsweise durch die Verwendung von Verzeichnisdurchlaufzeichen wie zum Beispiel „../“ im Dateinamen.


Richtige Umsetzung:


Durch Ersetzen der Funktion „send_file“ durch die sichere Funktion „send_from_directory“ von Flask wird das Risiko einer Verzeichnisüberquerung verringert. Es erlaubt nur das Abrufen von Dateien aus einem bestimmten fest codierten Verzeichnis (in diesem Fall „Exporte“).


  1. Anwendungsfall: Vergleich geheimer Token – Typ Jonglieren/Zwang

In einigen Programmiersprachen wie PHP und NodeJS ist es möglich, Vergleiche zwischen zwei verschiedenen Variablentypen durchzuführen, und das Programm führt im Hintergrund eine Typkonvertierung durch, um die Aktion abzuschließen.


Bei losen Vergleichen wird der Variablentyp nicht hinter den Kulissen überprüft und ist daher anfälliger für Typ-Jonglage-Angriffe. Allerdings wird die Verwendung loser Vergleiche an sich nicht als unsichere Codierungspraxis betrachtet – sie hängt vom Kontext des Codes ab. Und wiederum ist es für KI-Modelle schwierig, die Fälle auseinanderzuhalten.


KI-Tool-Vorschlag (Amazon CodeWhisperer): Das häufigste Beispiel für PHP-Typ-Jonglage sind lose Vergleiche von geheimen Tokens/Hashes, bei denen Benutzereingaben, die über eine JSON-Anfrage gelesen werden (die die Steuerung des Eingabetyps ermöglichen), zu einem losen Vergleich („ “) gelangen. ) anstelle einer strengen Eins („ =“).


Offenbar generiert CodeWhisperer lose Vergleichsbedingungen in Vorschlägen zur automatischen Vervollständigung:

Der lose Vergleich des Secret_token ermöglicht es einem Gegner, den Vergleich $data[„secret_token“] == „secret_token“ zu umgehen. Beim Senden eines JSON-Objekts mit dem Wert „secret_token“ als True (boolean) ist das lose Vergleichsergebnis ebenfalls True (sogar bei neueren PHP-Versionen).



Selbst wenn das Tool (mittels eines Kommentars) darauf hingewiesen wurde, den Scheck „sicher“ zu schreiben, wurde kein Codeausschnitt mit einem strengen Vergleich generiert –

Richtige Umsetzung:

Nur wenn ein strikter Vergleich („===“) angegeben wird, sind wir vor Typ-Jonglage-Angriffen geschützt.


  1. Anwendungsfall: Mechanismus „Passwort vergessen“ – Unicode-Case-Mapping-Kollisionen

Schwachstellen im „Passwort vergessen“-Mechanismus kommen in SaaS-Anwendungen sehr häufig vor und waren die Hauptursache für CVEs wie z CVE-2017-8295 (Wordpress), CVE-2019-19844 (Django) und CVE-2023-7028 (GitLab).


Insbesondere tritt eine Sicherheitslücke durch Unicode Case Mapping Collision auf, wenn Benutzereingaben in einem Vergleichsausdruck entweder in Groß- oder Kleinbuchstaben geschrieben werden, während der ursprüngliche Wert auch im Code verwendet wird. Einige unterschiedliche Zeichen führen bei der Transformation zum gleichen Zeichencode. Beispielsweise werden „ß“ und „SS“ beide in „0x00DF“ umgewandelt, wenn sie in Großbuchstaben geschrieben werden.


Solche Fälle werden normalerweise verwendet, um einige Sicherheitsprüfungen zu umgehen/in die Irre zu führen, indem die Vergleichsprüfung ausgetrickst und später der ursprüngliche Wert verwendet wird, der anders als erwartet ist. Diese Fälle sind schwer zu erfassen, insbesondere wenn viele Implementierungen möglich sind.


Vorschlag für ein KI-Tool (GitHub Copilot): Ein Mechanismus, der besonders anfällig für diesen Schwachstellentyp ist, ist der Mechanismus für vergessene Passwörter . Es ist üblich, die Eingaben des Benutzers bei der Durchführung der Prüfung in Groß- oder Kleinbuchstaben zu normalisieren (in E-Mail-Adressen oder Domänennamen), um unerwünschte Abweichungen zu vermeiden.


Ein Blick auf den von Copilot generierten Code für die Funktion „Passwort vergessen“ (unten) zeigt beispielsweise, dass er für dieses Problem anfällig ist.


Der Code von Copilot sucht zunächst nach der E-Mail-Adresse in Kleinbuchstaben:

Passwort vergessen – Vorschlag, die E-Mail-Adresse bei der Abfrage in Kleinbuchstaben zu schreiben


Wenn man dann den Rest des Prozesses durchführt, schlägt es Folgendes vor:

Passwort vergessen – Vorschlag, die ursprüngliche E-Mail-Adresse (nicht in Kleinbuchstaben) in send_email zu verwenden


Wie wir sehen können, generiert der Vorschlag zur automatischen Vervollständigung ein zufälliges Passwort und sendet es an die E-Mail-Adresse des Benutzers. Allerdings wird zunächst eine kleingeschriebene Version der E-Mail-Adresse des Benutzers zum Abrufen des Benutzerkontos verwendet und dann weiterhin die E-Mail-Adresse des ursprünglichen Benutzers „wie sie ist“ (ohne Umwandlung in Kleinbuchstaben) als Ziel der Wiederherstellungs-E-Mail verwendet.


Nehmen wir zum Beispiel an, der Angreifer besitzt den Posteingang für „[email protected]“ (das „K“ ist ein Kelvin-Sign- Unicode-Zeichen) und verwendet diese E-Mail für den „Passwort vergessen“-Mechanismus. Die E-Mails „[email protected]“ und „[email protected]“ entsprechen nicht „wie sie sind“, aber nach der Umwandlung in Kleinbuchstaben lauten sie:


Durch die Verwendung der E-Mail-Adresse „[email protected]“ werden die Kontoinformationen für „[email protected]“ abgerufen, aber das zurückgesetzte Passwort wird an den Posteingang des Angreifers („[email protected]“) gesendet, wodurch die Übernahme des „ [email protected]“-Konto.


Richtige Implementierung: Die zum Abrufen des Benutzerkontos verwendete E-Mail-Adresse muss genau mit der Wiederherstellungs-E-Mail-Adresse übereinstimmen (dh der Parameter send_email verwendet auch email.lower()).


Darüber hinaus wird vorsichtshalber empfohlen, den bereits auf dem Server gespeicherten Wert anstelle des vom Benutzer bereitgestellten Werts zu verwenden.


  1. Anwendungsfall: Generieren einer Konfigurationsdatei – Schlechte Sicherheitspraktiken

Ein weiteres Thema, das Autovervollständigungstools verwirren könnte, ist das Schreiben von Konfigurationsdateien, insbesondere für komplizierte Cloud-Infrastrukturen.


Der Hauptgrund dafür ist, dass es zwar Best-Practice-Richtlinien für die Sicherheit gibt, diese aber nicht von allen befolgt werden und in manchen Fällen sogar gegen sie verstoßen werden muss. Solche Codebeispiele könnten Eingang in den KI-Trainingsdatensatz finden. Daher verstoßen einige Autovervollständigungsausgaben gegen die empfohlenen Sicherheitsrichtlinien.


Im folgenden Beispiel erstellen wir eine Konfigurationsdatei für einen Kubernetes-Pod , die kleinste Ausführungseinheit in Kubernetes.


AI-Tool-Vorschlag (Blackbox AI-Codegenerierung): In diesem Beispiel beginnen wir mit der Erstellung einer Pod-Konfigurationsdatei.

Der Vorschlag erstellt den Abschnitt „Capabilities“ und fügt dem Container eine Linux-Kernel-Funktion (SYS_ADMIN) hinzu, was im Wesentlichen dem Ausführen des Pods als Root-Benutzer entspricht.


Richtige Implementierung: Ist es dann besser, den securityContext einfach wegzulassen? Nein, da dann standardmäßig viele andere Funktionen hinzugefügt werden, was gegen das Prinzip der geringsten Rechte verstößt.


Gemäß den Best Practices für die Sicherheit von Docker besteht die sicherste Einrichtung darin, zunächst alle Linux-Kernelfunktionen zu löschen und erst danach die für Ihren Container erforderlichen Funktionen hinzuzufügen.


Um die oben genannten Probleme zu beheben, werden im Abschnitt „Funktionen“ zunächst alle Funktionen entfernt und dann nach und nach die für unseren Container benötigten hinzugefügt.


  1. Anwendungsfall: Konfigurationsobjekte – Inkonsistenzen führen zu unsicherer Deserialisierung

    Viele Schwachstellen erfordern die Definition eines Konfigurationsobjekts, das entscheidet, wie die Anwendung mit der erforderlichen Komponente umgeht.


Das von uns ausgewählte Beispiel ist die JSON-Deserialisierungsbibliothek von Newtonsoft in C#, die je nach Konfiguration des JsonSerializerSettings-Objekts und insbesondere des TypeNameHandling sicher oder unsicher verwendet werden kann.


Während wir den Deserialisierungscode getestet haben, ist uns aufgefallen, dass die Definition des Konfigurationsobjekts ziemlich zufällig ist und subtile Änderungen dazu führen können, dass ein anderer Konfigurationssatz generiert wird.


Vorschläge für KI-Tools (Amazon CodeWhisperer): Die folgenden Beispiele zeigen ein inkonsistentes Verhalten, das nur auf den vom Entwickler verwendeten Methodennamen basiert:


Wir können zwei unterschiedliche Konfigurationen erkennen, die beide zu einer unsicheren JSON-Deserialisierung führen, da die TypeNameHandling-Eigenschaft „Auto“ und „ALL“ ist. Mithilfe dieser Eigenschaften kann das JSON-Dokument die deserialisierte Klasse definieren, sodass Angreifer beliebige Klassen deserialisieren können. Dies kann leicht für die Remotecodeausführung genutzt werden, beispielsweise durch Deserialisierung der Klasse „System.Diagnostics.Process“. Der einzige Unterschied im Originalcode des Entwicklers sind die Methodennamen.


Richtige Umsetzung:

Standardmäßig wird ein JsonSerializerSettings-Objekt mit „TypeNameHandling = TypeNameHandling.None“ instanziiert, was als sicher gilt. Daher verwenden wir den Standardkonstruktor von JsonSerializerSettings, der zu einem sicheren TypeNameHandling-Wert führt.


  1. Anwendungsfall „Sichere Codeanforderung“: Dateien lesen – Verzeichnisdurchquerung vermeiden Ähnlich wie im ersten Anwendungsfall, bei dem wir den anfälligen Code untersucht haben, der von Copilot zum Abrufen von Dateien erstellt wurde, versuchen wir hier, eine sichere Version eines Dateilesemechanismus anzufordern.


Vorschlag für ein KI-Tool (GitHub Copilot):

Wir können sehen, dass die Sicherheitsüberprüfung wirklich naiv ist und nur Punkt-Punkt-Schrägstrich-Sequenzen vermeidet, die für die Durchführung von Verzeichnisdurchquerungsversuchen erforderlich sind. Der Pfadparameter kann ein absoluter Pfad sein, der auf einen beliebigen Pfad im System verweist (z. B. /etc/passwd), wodurch der Zweck der Sicherheitsüberprüfung zunichte gemacht wird.


Richtige Umsetzung:

Hier ruft die Funktion secure_file_read einen (relativen) Dateinamenparameter zusammen mit einem Verzeichnis (safe_dir) ab, in dem sich der Dateiname befinden soll (und das nicht maskiert werden darf). Es erstellt einen absoluten Pfad durch die Verknüpfung von „safe_dir“ und „filename“ und erhält seine kanonisierte Form durch den Aufruf von „realpath“. Anschließend wird die kanonisierte Form des Ordners „safe_dir“ abgerufen. Dann werden die Dateiinhalte nur zurückgegeben, wenn der kanonisierte Pfad mit dem kanonisierten sicheren_Verzeichnis beginnt.


  1. Sichere Code-Anfrage – Datei-Upload – Liste eingeschränkter Erweiterungen Es ist ein häufiger Anwendungsfall, nur bestimmte Dateitypen basierend auf ihrer Erweiterung zum Hochladen zu akzeptieren.


Vorschlag für ein KI-Tool (Blackbox AI Code Generation): Im folgenden Beispiel haben wir den KI-Autovervollständiger gebeten, eine Funktion zu generieren, die mit dem Filtern gefährlicher Dateierweiterungen beauftragt ist.

Der vorgeschlagene Python-Code nimmt den Dateinamen, trennt die Erweiterung und vergleicht sie mit einer Liste gefährlicher Erweiterungen.


Auf den ersten Blick sieht dieser Codeausschnitt sicher aus. Allerdings verbieten die Dateinamenskonventionen von Windows , dass Dateinamen mit einem Punkt enden. Wenn Sie bei der Dateierstellung einen Dateinamen angeben, der mit einem Punkt („.“) endet, wird der Punkt verworfen. Das bedeutet, dass der generierte Filter unter Windows umgangen werden könnte, wenn eine Datei mit einer schädlichen Erweiterung gefolgt von einem Punkt übermittelt wird (z. B. „example.exe“).


Richtige Implementierung: Normalerweise ist der bessere Ansatz die Verwendung einer Whitelist , die die Dateierweiterung nur mit zulässigen Erweiterungen vergleicht. Da die Tools jedoch einen Blacklist-Ansatz verfolgten (was in manchen Fällen erforderlich ist), werden wir eine sicherere Blacklist-Implementierung skizzieren:


Im folgenden Snippet entziehen wir dem Benutzer so viel Kontrolle wie möglich über die Eingabe, indem wir die Erweiterung von allen nicht alphanumerischen Zeichen entfernen und sie mit einem neu generierten Dateinamen verketten.


Fazit: Mit Vorsicht verwenden Automatische Co-Programmierer eignen sich hervorragend, um Ideen vorzuschlagen, Code automatisch zu vervollständigen und den Softwareentwicklungsprozess zu beschleunigen. Wir gehen davon aus, dass sich diese Tools im Laufe der Zeit weiterentwickeln, aber wie die Anwendungsfälle in diesem Beitrag zeigen, müssen KI-Lösungen mit automatischer Vervollständigung vorerst viele Herausforderungen meistern, bis wir uns beruhigen und ihrer Ausgabe vertrauen können, ohne sie noch einmal zu überprüfen Käfer.


Eine mögliche Möglichkeit, die Wahrscheinlichkeit der Einführung von Schwachstellen zu verringern, besteht darin, das KI-Codegenerierungstool gezielt anzufordern, sicheren Code zu generieren. Obwohl dies in manchen Anwendungsfällen nützlich sein kann, ist es nicht narrensicher – die „scheinbar sichere“ Ausgabe sollte dennoch manuell von jemandem überprüft werden, der sich mit Softwaresicherheit auskennt.


Empfehlungen des JFrog-Sicherheitsforschungsteams Um Ihre Software zu schützen, empfehlen wir sowohl die manuelle Überprüfung des von generativen KI-Tools erstellten Codes als auch die Integration von Sicherheitslösungen wie Static Application Security Testing (SAST), die Schwachstellen zuverlässig entdecken können, während Sie arbeiten. Wie im obigen Beispiel zu sehen ist, können SAST-Tools die im Anwendungsfall Nr. 3 beschriebene Unicode-Fallzuordnungskollision warnen und abfangen. Dieser proaktive Ansatz ist für die Gewährleistung der Sicherheit Ihrer Software unerlässlich.


Bleiben Sie mit der JFrog-Sicherheitsforschung auf dem Laufenden. Die Ergebnisse und Forschungen des Sicherheitsforschungsteams spielen eine wichtige Rolle bei der Verbesserung der Sicherheitsfunktionen der Anwendungssoftware der JFrog-Plattform.


Verfolgen Sie die neuesten Entdeckungen und technischen Updates des JFrog Security Research-Teams auf unserer Forschungswebsite und auf X @JFrogSecurity .