Sicherheitsaudit: fünf schwere Treffer im eigenen Code
Einmal alles durchsehen, aufschreiben, einstufen, abarbeiten. Was ich gefunden habe, was mich am meisten geärgert hat und warum kein einziger Fund exotisch war.
1051 Wörter · 5 Min. Lesezeit
- sicherheit
- yurna
- audit
Der Vorsatz stand seit anderthalb Jahren: einmal systematisch durch das ganze System gehen, statt punktuell zu verbessern. Dieses Wochenende habe ich es gemacht — mit einer Liste, mit Einstufungen, mit Status pro Punkt.
Ergebnis: fünf schwere Befunde, dreizehn mittlere, elf leichte. Alle fünf schweren sind behoben. Und keiner davon war raffiniert.
Die fünf schweren Befunde
1. Ein Dateidownload ohne Zugriffsprüfung. Eine Route, über die man Anhänge aus Support-Tickets abrufen konnte. Sie prüfte, ob der angeforderte Pfad innerhalb des Ablageverzeichnisses liegt — also gegen Pfadmanipulation. Sie prüfte nicht, ob der Anfragende überhaupt Zugriff auf diesen Server haben darf.
Die einzige Ticket-Route ohne Wache. Alle anderen hatten sie. Das ist das Muster, das ich seitdem am häufigsten sehe: nicht eine falsche Prüfung, sondern eine fehlende an genau einer Stelle.
2. Rechte, die zu weit gefasst waren. Ein Zugang, der historisch mehr durfte, als er brauchte — entstanden, weil beim Einrichten alles funktionieren sollte und danach niemand eingegrenzt hat.
3. Ein Geheimnisvergleich, der nicht zeitkonstant war. Beim Vergleich zweier Zeichenketten mit dem üblichen Operator bricht der Vergleich beim ersten Unterschied ab. Aus der Antwortzeit lassen sich theoretisch Rückschlüsse ziehen. In der Praxis über ein Netzwerk schwer auszunutzen — aber die Behebung ist eine Zeile, und dann muss man nicht abwägen.
4. Ein Wettlauf beim Einlösen von Codes. Prüfen, ob ein Code gültig ist, und ihn danach als benutzt markieren, waren zwei getrennte Schritte. Wer zweimal gleichzeitig einlöst, kann beide Prüfungen passieren, bevor die erste Markierung geschrieben ist. Behoben über eine Sperre, die genau einer bekommt, plus eine eindeutige Bedingung in der Datenbank als zweite Absicherung.
5. Fremde Zugriffstoken im Klartext. Der Punkt, den ich im Frühjahr des Vorjahres schon angegangen war und der an einer weiteren Stelle noch offen war.
Was mich am meisten geärgert hat
Nicht die Befunde selbst — es ist der Sinn eines solchen Durchgangs, etwas zu finden.
Was mich geärgert hat, ist, dass jeder einzelne Fund eine Abweichung von etwas war, das ich anderswo richtig gemacht habe. Die Ticket-Route ohne Wache: Alle anderen hatten sie. Der Vergleich ohne Zeitkonstanz: An zwei anderen Stellen zeitkonstant. Der Wettlauf: An vergleichbarer Stelle abgesichert.
Es war kein Wissensproblem. Es war ein Konsistenzproblem — und das ist ein anderer Fehlertyp mit anderen Gegenmitteln.
Was ich daraus baue
Sicherheitsprüfungen an eine Stelle, die man nicht umgehen kann. Nicht in jeder Route, sondern als Schicht vor einer Gruppe. Eine neue Route ist dann automatisch geschützt.
Eine Prüfliste für neue Routen. Fünf Fragen, die ich beantworte, bevor etwas dazukommt: Wer darf das? Wo wird das geprüft? Was passiert, wenn die Prüfung nicht möglich ist? Werden Eingaben validiert? Steht etwas Sensibles in der Antwort?
Eine Suche nach Mustern statt nach Fehlern. Statt „gibt es hier eine Lücke" die Frage „an welchen Stellen kommt dieses Muster vor, und ist es überall gleich behandelt?" — genau so habe ich die Ticket-Route gefunden.
Infobox
Ein Wort zum Verfahren, weil das die Frage ist, die mir am häufigsten gestellt wird: Ich bin nicht nach Angriffsarten vorgegangen, sondern nach Eingängen. Jede Route, jeder Befehl, jeder Endpunkt, jeder Ereignishandler, der Daten von außen bekommt. Für jeden davon dieselben fünf Fragen. Das ist mühsam und findet Dinge, die einem beim Nachdenken über Angriffe nie einfallen.
Die Punkte, die ich bewusst offen lasse
Ehrlichkeit gehört in so einen Bericht.
Vollständige Fehlerobjekte in Protokollen. In einem Großteil meiner Fehlerbehandlungen wird das komplette Fehlerobjekt ausgegeben. Das kann Werte enthalten, die dort nicht hingehören. Umzustellen wäre eine Durchsicht von rund neunzig Stellen — es steht als bekannter Punkt auf der Liste.
Ein Schlüssel im Adressteil einer Einlieferungsschnittstelle. Weil die aufrufende Seite es so schickt und ich sie nicht ändern kann. Der Kopfzeilen-Weg ist inzwischen zusätzlich möglich; der alte bleibt aus Kompatibilität erlaubt.
Keine echte Rotation von Zugriffstoken. Sie werden verschlüsselt gespeichert und laufen ab, aber es gibt keinen automatischen Wechsel.
Diese drei Punkte stehen mit Begründung in der Liste. Das ist der Unterschied zwischen „offen" und „übersehen", und es ist der Grund, warum ich so einen Durchgang überhaupt dokumentiere.
Wie ich die Liste geführt habe
Ein Durchgang ohne Dokument ist ein Nachmittag mit gutem Gefühl. Die Liste ist der eigentliche Ertrag, und sie hat eine feste Form.
Pro Befund: Fundort mit Datei und Zeile. Einstufung in drei Stufen. Beschreibung in zwei Sätzen — was ist falsch, was könnte passieren. Status mit Datum. Und bei behobenen Punkten: wie geprüft wurde, dass die Behebung wirkt.
Die letzte Angabe ist die, die am häufigsten fehlt. „Behoben" ohne Prüfung ist eine Behauptung. Bei einer fehlenden Zugriffsprüfung heißt Prüfung: einmal ohne Berechtigung aufrufen und sehen, dass es abgelehnt wird — nicht: den Code lesen und zufrieden sein.
Die Einstufung habe ich bewusst grob gehalten: hoch heißt, jemand kommt an Daten oder Rechte, die ihm nicht zustehen. mittel heißt, es erleichtert einen Angriff oder verletzt eine Konvention. niedrig heißt, es ist unschön.
Feinere Skalen habe ich probiert und wieder verworfen — sie führen zu Diskussionen über die Einstufung statt über die Behebung.
Was ich beim nächsten Mal anders mache
Früher anfangen und kürzer machen. Ein Durchgang alle sechs Monate über einen Teilbereich ist besser als ein großer alle zwei Jahre. Die Befunde sind dann frischer, und man behebt sie, während der Kontext noch da ist.
Mit den Eingängen anfangen, nicht mit dem Code. Eine Liste aller Stellen, an denen Daten von außen ins System kommen, ist der beste Ausgangspunkt. Ich hatte sie vorher nicht und musste sie erstellen — das war die halbe Arbeit und der wertvollste Teil.
Die Liste behalten. Sie ist beim nächsten Durchgang die Grundlage: Was war offen? Ist es noch offen? Ist etwas Neues dazugekommen, das dem Muster eines alten Befunds entspricht?
Genau dieser letzte Punkt hat sich wenige Tage später bezahlt gemacht, als ich einen zweiten Durchgang gemacht habe — und dabei denselben Fehlertyp an einer benachbarten Stelle gefunden habe.
Was ich anderen mitgeben würde
Mach es, bevor jemand fragt. Es gab keinen Vorfall. Es gab keine Meldung. Es gab nur die Frage, was jemand mit meiner Datenbankdatei anfangen könnte, wenn er sie hätte.
Schreib alles auf, auch das, was du nicht behebst. Ein bekannter, begründeter offener Punkt ist ein völlig anderer Zustand als eine unbekannte Lücke.
Rechne mit Konsistenzfehlern, nicht mit Wissenslücken. Wer weiß, wie es richtig geht, macht es meistens auch richtig — außer an der einen Stelle, die später dazukam.