Github Copilot & Co.

Software zu schreiben ist keine einfache Aufgabe. Das wird uns spätestens dann immer wieder klar, wenn täglich auf unseren Geräten neue Updates mit Bugfixes zur Verfügung stehen und darauf warten, eingespielt zu werden. Wer kennt es nicht? Und wer sich schon getraut hat, in die Tiefen der Release Notes der jeweiligen Updates einzutauchen, findet neben ganz harmlosen sogenannten „Quality of Life“ Verbesserungen ab und zu auch einige mehr Besorgnis erregende Einträge. Darunter oft auch Sicherheitsschwachstellen, welche aus den jeweiligen Applikationen entfernt wurden.

Doch warum ist es so schwer sichere Software zu schreiben? Mit modernen Tools und künstlicher Intelligenz (KI), welche heutigen Entwicklern zur Verfügung stehen, sollte es doch eigentlich immer einfacher werden sicherheitskritische Bugs zu erkennen und zu verhindern. Andererseits ist aber auch nicht ganz klar ob solche Tools eventuell eine ganz neue Quelle von Bugs und Schwachstellen sein kann.

In diesem Zusammenhang wollte ich mich auf ein ganz besonderes Tool beschränken und rausfinden wie einfach es ist mit der Hilfe solcher Tools Schwachstellen in Applikationen unbewusst einzubauen.

Welche Tools verwenden Entwickler und was ist Github Copilot?

Zwischen Entwicklern fallen oft Aussagen wie „Lieber 10 Stunden an einem Automatisierungsskript arbeiten, als die gleiche Arbeit in 10 Minuten manuell ausführen“. Programmierer sind daher sehr darauf bedacht, eigene Tools zu entwickeln, um sich die Arbeit des Codens und damit das eigene Leben etwas einfacher zu machen.

Seit der Einführung von mehr raffinierten Entwickleroberflächen wie VS-Code existieren Technologien wie Code-completion oder Code-Analyzer Tools, welche den Code schon beim Schreiben nach Bugs und Fehlern durchsuchen. Heutzutage muss man zum Glück nicht mehr Lochkarten mühsam nach Fehlern durchsuchen, sondern kann sich mit einem Klick ganz einfach anzeigen lassen, wo kritische Stellen und Fehler vorhanden sind. Oft werden im gleichen Atemzug Beispiele vorgeschlagen, wie einzelne Code-Stücke besser geschrieben werden könnten.

Solche Tools, welche den Code (mehr oder weniger oberflächlich) schon in der Entwicklungsumgebung oder spätestens bei der Integration in die Codebase analysieren, gibt es schon seit langem. Was aber durch die wachsende Relevanz von KI-Tools wie ChatGPT und Github Copilot hinzugekommen ist, ist eine ganz neue und viel mächtigere Fähigkeit: Mit nur einer kurzen Beschreibung können innerhalb kürzester Zeit mehrere Zeilen Code und sogar ganze Funktionen und Features generiert werden.

Bei Github Copilot handelt es sich um ein Produkt von Github, welches im Juni 2021 veröffentlicht wurde. Somit ist es eine Art Vorreiter von ChatGPT, welches zur Zeit in aller Munde ist. Im Gegensatz zu ChatGPT kann Copilot aber nur dazu genutzt werden, Code aus natürlicher Sprache zu generieren. Also zum Beispiel Anweisungen wie „Erstelle ein Script zur Passwortvalidierung“. Was Copilot aber mit ChatGPT gemeinsam hat, ist der Entwickler, die amerikanische Firma OpenAI.

Github Copilot und das zugrundeliegende Cortex Modell können eines gut: Code generieren, der am wahrscheinlichsten ist. Das bedeutet jedoch keineswegs, dass dieser Code auch der beste Weg ist, um die gegebene Aufgabe zu lösen. Solche Modelle können nämlich nichts Neues erfinden. Sie können nur Code generieren, den sie schon gesehen haben. Copilot wurde auf viel Code trainiert, darunter aber sicherlich auch sehr viel schlechtem Code. Es stellt sich also die Frage: Wie gut/schlecht ist der Code von Copilot?

Tests

Um uns selbst mit dem Tool auseinander zu setzten, wollen wir nun einen kleinen Test durchführen.

Die Idee: Eine simple Webapp in Python.

Die Tools: Eine Entwicklungs-Umgebung mit Github Copilot als Plugin

Um herauszufinden, wie es mit der Sicherheit von Github Copilot aussieht. bauen wir eine Login Funktion ein, welche überprüft, ob ein Benutzername mit dem gegebenen Passwort in einer Datenbank hinterlegt ist. Hier gibt es sicher vieles, was man falsch machen kann, und wir wollen testen, wie Copilot damit umgeht.

Mit Github Copilot als Plugin im Gepäck können wir nun loslegen. Als erstes müssen einige Python Packages importiert werden, welche wir später noch brauchen. Hier ist Copilot noch nicht wirklich hilfreich. Da noch kein Code vorhanden ist und der Name der App keinen Hinweis auf die gewünschte Funktionalität preisgibt, werden einfach beliebige Vorschläge getätigt. Zum Glück wissen wir aber schon, was wir brauchen werden und können Copilot die Arbeit abnehmen. Flask benötigen wir für die Grundfunktionalität einer Webapp und pymysql wird für den Zugriff auf die Datenbank benötigt.

Als nächstes wollen wir dem User die Möglichkeit geben, sich mit einem Usernamen und einem Passwort über die URL /login anzumelden. Auch Copilot hält das für eine gute Idee: Mit nur einem kleinen Kommentar ‘# login url’ im Code weiss Copilot schon, wo anzusetzen ist, und schlägt uns auf der nächsten Zeile in dunkelgrau vor, wie die nächste Codezeile aussehen könnte.

Das Einzige, was fehlt, ist eine Bestätigung des Vorschlags unsererseits durch TAB.

Nun wollen wir den Usernamen sowie das Passwort aus den Parametern auslesen. Mit Flask ist das sehr einfach möglich und Copilot weiss genau, wie das gemacht werden muss. Solche Login Parameter auszulesen ist für Copilot nichts Neues. Wir bekommen automatisch die zwei Vorschläge die Parameter in Variablen zu hinterlegen. Wir nehmen diese Vorschläge von Copilot gerne an.

Als nächstes müssen wir uns mit der Datenbank verbinden und die Eingaben des Users überprüfen. Auch da ist Copilot uns einen Schritt voraus und bietet eifrig neue Vorschläge an. Wir nehmen diese der Reihe nach einfach mal so an. Zuerst verbinden wir uns mit der gewünschten Datenbank und kreieren einen sogenannten Cursor, mit dem wir Anfragen auf der Datenbank ausführen können.

Als letztes kommt der wichtigste Punkt. Wir müssen nun eine Anfrage an die Datenbank machen und überprüfen, ob der User mit dem gegebenen Passwort so auch existiert. Falls ja, leiten wir den User auf eine andere Unterseite weiter. Falls die Daten nicht stimmen, passiert nichts. Da wir hier nur unsere eigene kleine Webapp zum Testen bauen, reicht dies aus.

Abgesehen davon, dass wir uns mit dem User ‘root’ mit Passwort ‘root’ an der Datenbank anmelden, was sicherlich nicht ideal ist, stossen wir hier auch schon auf den ersten kritischen Fehler, der sich durch Copilot in unser Programm eingeschlichen hat.

Ein geschultes Auge wird wohl schnell erkennen, dass es sich hier, beim Vorschlag von Copilot, um eine klassische SQL-Injektion Schwachstelle handelt. Genau diese Schwachstelle werden wir gleich versuchen auszunutzen. Aber zuerst müssen wir die Applikation abschliessen.

Da es sich nur um eine Test-Applikation handelt, überprüfen wir die eingegebenen Daten und leiten den User bei einem korrekten Login auf eine die Seite (/success) weiter, welche „Login Success“ anzeigt. Auf der Startseite der Applikation fügen wir zusätzlich noch ein HTML-Formular ein, um unsere Applikation im Browser testen zu können. Bis jetzt haben wir nur die ersten Zeilen selbst geschrieben. Den Rest hat Copilot für uns geschrieben.

Wenn wir das Programm starten, sehen wir wie erwartet die Applikation. Zugegeben ist es nicht die schönste Applikation, aber für unsere Zwecke reicht es aus.

In der lokalen Datenbank legen wir den Benutzer „john“ mit dem zugegeben unsicheren Passwort „password123“ an.

Bei einem Login mit falschen sowie gültigen Benutzerdaten sehen wir, dass die Applikation wie erwartet funktioniert. Sie leitet den Benutzer auf die Seite /success weiter. Bei einem ungültigen Login werden wir nicht weitergeleitet. In den Logs der Applikation werden die Eingabedaten sowie einfachheitshalber die ausgeführte Datenbank-Anfrage aufgelistet.

Wie vorher erwähnt läuft bei unserer Applikation jedoch nicht alles so wie erwartet. Auf Zeile 18 unseres mit Copilot erstellten Codes wird nämlich nach Einsetzen der Variablen username und password folgende Anfrage in der SQL Datenbank ausgeführt:

SELECT * FROM user WHERE username="username" AND password="password"

Wenn nun ein Benutzer zum Beispiel beim Benutzernamen oder beim Passwort “ OR 1=1– einsetzt, sieht die Anfrage folgendermassen aus:

SELECT * FROM user WHERE username="username" AND password="" OR 1=1-- "

Anstelle das nur der Benutzename und das Passwort überprüft werden, ist das Login auch korrekt falls 1=1. Mit den beiden Bindestrichen kommentieren wir den Rest der Anfrage aus und verhindern somit einen Syntaxfehler in der Anfrage. Die Anfrage kann also direkt durch unsere Eingaben beeinflusst werden und der Login Prozess, so wie er jetzt existiert, kann einfach umgangen werden. Somit kann man auch ohne gültige Logindaten auf die „Success“-Page gelangen.

Weitere Tests

Obwohl wir bei der Python Webapp in wenigen Sekunden ein funktionierendes Programm erstellen konnten, hat bereits ein kleiner Versuch gezeigt, dass den Vorschlägen von Copilot nicht immer zu trauen ist.

Weitere Versuche haben zusätzliche Probleme von Copilot zum Vorschein gebracht. Zum Beispiel werden auch zum Teil API-Keys oder andere sensible Daten von Copilot direkt im Code vorgeschlagen.

Ob diese API Codes nun wirklich gültig sind oder nicht, die Verwendung von solchen Keys im Code selbst ist keine gute Idee. Sensible Daten sollten grundsätzlich nie im Code selber hinterlegt werden, sondern durch sogenannte Environment Variablen in die Applikation einfliessen.

Auch ist nicht ganz klar, wie Github Copilot den eigenen Code verarbeitet und ob dieser wieder in das Modell einfliesst, um Copilot zu verbessern. Dies ist bei solchen grossen Text-Modellen, wie ChatGPT, nämlich oft der Fall.

Fazit

Es gibt viele Gründe, warum Github Copilot für Programmierer ein wertvolles Tool sein kann. Für sogenannten „Boilerplate Code“, also Code, der an vielen Stellen ohne grosse Variation wiederholt werden muss, können Tools und Plugins, welche Codevorschläge generieren, viel Zeit und Energie sparen. Diese Zeit können Entwickler dann in wichtigere und komplexere Funktionen stecken. So zum Beispiel in sicherheitskritische Funktionen einer Applikation.

Auf der anderen Seite gibt es auch viele Risiken und viele Fragen ohne konkrete Antworten, die beim Gebrauch von KI-Programmierhilfen auftreten. So gibt es zum Beispiel bei vielen solchen KI-Modellen keine Übersicht, woher die Trainingsdaten genau kommen. Wie schon im Beispiel gesehen ist es sehr einfach, dass sich Fehler und sicherheitskritische Bugs durch solche Tools in den Code einschleusen können.

Solche KI-Modelle zeigen aber auch viel Potenzial. In Zukunft könnten beispielweise neue Modelle entwickelt werden, die speziell darauf trainiert sind, Security-Fehler in Applikationen zu erkennen und diese zu verhindern.

Schlussendlich braucht es den Menschen also doch noch. Als Architekten ihrer Programme und Systeme müssen Entwickler auch bei Verwendung von KI-Tools stets achtsam bleiben. Automatisch generierter Code sollte nicht einfach ungeprüft in kritische Systeme einfliessen. Obwohl die Zusammenarbeit von Mensch und KI viele neue Möglichkeiten eröffnet und viel Potenzial mit sich bringt, muss man als Programmierer doch verstehen was der generierte Code genau macht. Schlussendlich liegt es in den Händen der Entwickler, alle ihre Werkzeuge verantwortungsbewusst einzusetzen.

Quellen

Informationen zum Autor: Annika Salzmann

Ein Programmierkurs hat bei mir als Kind die Leidenschaft für Computer und IT geweckt. Während meinem Informatikstudium habe ich dann meine Faszination für die IT-Security entdeckt. Seit Herbst 2022 darf ich nun bei der goSecurity, begleitend zu meinem Studium, meinem Wissensdrang nachkommen und meine Kenntnisse im Bereich Softwareentwicklung einbeziehen.