Genauso wie sich die Entwicklergemeinschaft in den letzten Jahren verändert hat, müssen sich auch Penetrationstests und die Pentester an die sich ändernden Gegebenheiten anpassen. Wie Penetrationstests in einer agilen Welt aussehen können, soll im folgenden Artikel über agile Penetrationstests aufgezeigt werden.
Herausforderungen für Penetrationstests in der agilen Entwicklung
Gerade in der agilen Entwicklung funktionieren die klassischen Pentests nicht. Mit klassisch meine ich: nachdem die Software fertig entwickelte wurde, fand ein Penetrationstest statt. Etwaige Fehler wurden dann anschließend behoben. Zum einen ist das sowieso eine ungünstige Vorgehensweise: Wenn in diesem abschließendem Penetrationstest grundlegende Fehler in der Sicherheits- bzw. Software-Architektur gefunden wurden, war die Behebung sehr aufwändig.
Mit dem iterativen Vorgehen in der agilen Entwicklung lässt sich dieses Verfahren jedoch grundsätzlich nicht vereinbaren. Der finale Endpunkt mit einem klassischen Release-Zyklus ist heute immer seltener vorhanden. Natürlich macht auch ein umfassender Penetrationstest nach jedem Sprint keinen Sinn. Die Kosten würden den Nutzen nicht rechtfertigen.
Noch schwieriger wird es im Bereich von Continous Integration und Continous Delivery bzw. Deployment. Bei einem mehrmals täglichen Deployment in die produktive Umgebung kann ein klassischer, manueller Pentest nicht mehr stattfinden.
Agile Penetrationstests: wie sehen diese aus?
Um diesen Herausforderungen zu begegnen, muss sich auch die Rolle des Pentetrationstesters ändern. Der Fokus geht weg von dem reinem technischen „Hacker“, hin zu einem Berater, der seine Angriffsexpertise in den Entwicklungsprozess einbringt.
In folgenden Bereichen ist der Pentester integriert:
Sichere Software-Architektur: Hier muss die Erfahrung eingebracht werden, um grundlegende Probleme beim Design der Software-Architektur zu vermeiden.
Evil User Stories: Hier hat der Pentester dafür zu sorgen, dass die Anforderungen an die Sicherheit und mögliche Angriffsszenarien für die Entwickler sichtbar gemacht werden.
Test-Automatisierung: Gerade im CI/CD-Umfeld sind automatisierte Tests notwendig, um zumindest die grundlegende Funktionalität der Sicherheitsfunktionen zu gewährleisten.
Manuelle Tests: Ohne die geht es auch in einer agilen Welt nicht.
Schauen wir uns die einzelnen Punkte im Detail an.
Von Anfang an: die sichere Software-Architektur
Hier hat der Pentester vor allem eine beratende Tätigkeit. Seine Expertise als Angreifer muss in den grundlegenden Design-Entscheidungen eingebracht werden. Leider werden hier häufig noch Fehler gemacht, die Sicherheitslücken implementieren, welche nur unter sehr hohen Aufwänden behoben werden können.
Ein typisches Beispiel ist das Berechtigungsmanagement. Dies ist vor allem ein Problem, welches bis vor kurzem hauptsächlich ältere Rich-Clients betroffen hat. Der Fehler lag darin, dass eine Zwei-Schicht-Architektur gewählt wurde. Die Anwendung griff also meist mit einem generischen User direkt auf die Datenbank zu. Alle sicherheitskritischen Funktionen wurden damit auf den Client ausgelagert und konnten vom Angreifer ohne größeren Aufwand vollständig umgangen werden. Wir haben übrigens vor einiger Zeit schon darüber gesprochen, welche Möglichkeiten man hat, eine Zwei-Schicht-Architektur abzusichern.
Leider nimmt diese Art von Design-Problem in letzter Zeit wieder zu. Das liegt vor allem daran, dass in mobilen Anwendungen oder mit modernen JavaScript-Frameworks wie Angular wieder so viel Logik auf den Client ausgelagert werden kann, dass wir vermehrt Fälle hatten, wo das Berechtigungsmanagement clientseitig implementiert wurde.
Der Penetrationstester hat hier also die Aufgabe sicherzustellen, dass solche grundlegenden Designfehler nicht gemacht werden.
Evil User Stories
Für Entwickler ist es meist schwierig, zum einen immer die Sicherheitsanforderungen umzusetzen und zum anderen überhaupt zu wissen, welche Angriffsszenarien möglich sind. Ist ja auch klar: die Aufgabe eines Programmierers ist es, ein Werk zu schaffen und nicht, dieses kaputt zu machen. Hinzu kommt, dass Entwickler meist schon genug damit beschäftigt sind herauszufinden, wie die „richtigen“ User Stories implementiert werden sollen.
Hier hat der Penetrationstester im Prinzip zwei Aufgaben. Zum einen sollte er wieder die Angreiferbrille aufsetzen und ein Risiko-Management etablieren. Schön wäre natürlich ein vollständiges Threat Modelling – für die meisten Projekte ist dafür aber leider weder Zeit noch Geld.
Ein Ansatz ist es zu fragen, was das Schlimmste ist, das passieren kann. Der worst case, der eintreten kann, ist ein guter Ansatzpunkt um sicherzustellen, dass die Software zumindest nicht grundlegend scheitert. Der worst case ist natürlich von Anwendung zu Anwendung unterschiedlich. In einer Software zur Buchhaltung hat die Integrität einen hohen Stellenwert, in einer Produktionssteuerung vor allem die Verfügbarkeit.
Nachdem das soweit klar ist, kann der Pentester dazu übergehen, die Evil User Stories zu formulieren.
Ein Beispiel wäre:
Als Angreifer
möchte ich im Dokumentenmanagement auf alle Daten zugreifen,
um einen Wettbewerbsvorteil zu erhalten.
Das ist natürlich sehr generisch formuliert, soll aber aufzeigen, worauf die späteren Entwickler dann bei der Implementierung des DMS achten sollten.
Sicherheit in den automatisierten Tests
Gerade im CI/CD-Umfeld ist die Implementierung von automatisierten Softwaretests nötig und auch sinnvoll. Eine entsprechend ausgestatte Build Pipeline hilft enorm, dass zumindest keine groben Schnitzer passieren können und gibt auch den Entwicklern die Sicherheit, dass alles soweit funktioniert.
In diesem Bereich hat der Pentester unterschiedliche Punkte sicherzustellen. Zum einen ist er dafür verantwortlich, dass für alle relevanten Bereiche überhaupt Tests implementiert sind. Dabei muss er systematisch für alle sicherheitskritischen Funktionen entsprechende Tests vorschlagen.
Viel wichtiger ist noch, dass bei agilen Penetrationstests sichergestellt werden muss, dass die Tests auch sinnvoll sind. Einen Web-Applikationsscanner beispielsweise ohne Zugangsdaten auf eine Anwendung loszulassen, führt zu vollständig irrelevanten Ergebnissen. Im besten Fall findet man dann noch fehlende Security-Header. Das ist natürlich besser als nichts, aber massive Verschwendung von Ressourcen.
Er hat also dafür zu sorgen, dass alle externen Tools für die Testautomatisierung (z.B. den OWASP ZAP) so konfiguriert sind, dass sie auch tatsächlich testen. Das beinhaltet die Einrichtung der Authentifizierung, aber auch die Berücksichtigung von anderen sicherheitstechnischen Funktionen wie z.B. CSRF-Token.
Zusätzlich muss er die Bereiche definieren, für die nur selbst entwickelte Tests sinnvoll sind. Ein Beispiel hierzu sind die Tests des Berechtigungsmanagements. Es ist ohne manuellen Eingriff nahezu unmöglich, für ein Computerprogramm festzustellen, ob auf eine Funktion zugegriffen werden darf oder nicht. Hier muss er die Entwickler dabei unterstützen, für ebensolche Funktionen eigene Testverfahren zu implementieren.
Grenzen der Automatisierung
Wie die Sicherheitslücke auf Facebook im Zusammenhang mit der „View As“-Funktion vor kurzem gezeigt hat, ist das zwar nicht ausreichend, aber dennoch ein sinnvoller Ansatz. Den Software-Entwicklern muss einfach klar sein, dass das manuelle Testen nicht verschwinden kann und eine Automatisierung von allen möglichen Tests schlicht und ergreifend nicht passieren kann.
Es gibt verschiedene Arten von Sicherheitslücken, die automatisiert einfach nicht gefunden werden können. Ein klassisches Beispiel sind Fehler in der Geschäftslogik. Folgendes (sehr stark vereinfachte) Beispiel aus einem Audit, den ich vor ein paar Jahren hatte, soll das verdeutlichen:
if($_SESSION['username']) { $username = $_SESSION['username']; $tenant_id = $_SESSION['tenant_id']; } if ($_POST['username']) { $username = $_POST['username']; $tenant_id = $_POST['tenant_id']; } $user = load_user($username); if($_POST['action'] == "login") { // Check auf Passwort und Mandantenberechtigung } else if($_POST['action'] == "changeTenant") { // Check ob eingeloggt und für Mandanten berechtigt $tenant_id = $_POST['tenant_id']; } init_user($user, $tenant_id);
PHP-Kenntnisse sind hier natürlich von Vorteil. Sie können ja ein wenig darüber nachdenken, wo das Problem liegt und uns auf Twitter (@securai) schreiben 🙂
Fazit
Die beratenden Tätigkeiten eines Penetrationstesters nehmen in einer agilen Welt zu. Das klassische Hingehen -> Kaputtmachen -> Abhauen nimmt immer mehr ab. Die Aufgabe des Pentesters ist es heutzutage, bereits aktiv im Entwicklungsprozess zu unterstützen und sein Angreifer-Know-How einzubringen.
Dazu gehören bereits die Beratung in der Software-Architektur sowie das Definieren von Evil User Stories. Auch ist es seine Aufgabe, sinnvolle, automatisierte Tests für die Build Pipeline bereitzustellen. Manuelle Tests sind natürlich nach wie vor wichtig, da nicht alles in einen Test gegossen werden oder von Applikationsscannern gefunden werden kann.
Wenn Sie die Herausforderung, die Sicherheit in den agilen Entwicklungsprozess zu integrieren, annehmen wollen, sind wir der richtige Ansprechpartner für Sie. Eine kurze Kontaktanfrage genügt und wir beraten Sie sehr gerne zum Thema agile Penetrationstests.