fail-closed als Grundhaltung
Wenn eine Prüfung nicht durchgeführt werden kann — verweigern oder durchlassen? Die Antwort hängt davon ab, was schiefgeht, und die Unterscheidung ist eine der nützlichsten, die ich kenne.
941 Wörter · 5 Min. Lesezeit
- sicherheit
- architektur
- meinung
Es gibt eine Frage, die in fast jedem System auftaucht und die man leicht übersieht: Was passiert, wenn eine Prüfung selbst fehlschlägt?
Nicht, wenn sie negativ ausfällt — das ist klar. Sondern wenn sie nicht durchgeführt werden kann. Die Datenbank antwortet nicht, der externe Dienst ist weg, eine Bibliothek wirft einen unerwarteten Fehler.
Zwei Antworten sind möglich. Verweigern, weil man es nicht besser weiß. Oder durchlassen, weil man niemanden grundlos aussperren will. Beide sind manchmal richtig, und die Unterscheidung ist eine der nützlichsten Denkhilfen in meinem Werkzeugkasten.
Wo ich verweigere
Bei jeder Authentifizierung. Kein Token, ungültiges Token, Prüfung nicht möglich — Zugriff verweigert. Ohne Ausnahme.
Dazu gehört ein Fall, den man leicht übersieht: Wenn gar kein Token konfiguriert ist. Meine Verwaltungsschnittstelle startet dann nicht. Es gibt keinen Zustand, in dem sie „vorläufig offen" läuft. Genau daraus entstehen die Fälle, in denen jemand eine Verwaltung ungeschützt im Internet stehen hat, weil beim Einrichten etwas übersprungen wurde.
Bei jeder Rechteprüfung. Wenn ich nicht feststellen kann, ob jemand etwas darf, darf er es nicht.
Bei Sperren. Wenn ich eine Sperre nicht bekomme, führe ich die Aktion nicht aus. Bei etwas, das nur einmal passieren darf — eine Einlösung, eine Auszahlung, eine Zustellung —, ist Doppelausführung schlimmer als Nichtausführung.
Bei geplanten Aufgaben mit Geheimnis. Ein Aufruf, der von außen ausgelöst wird und ein Geheimnis mitbringen muss, wird abgelehnt, wenn das Geheimnis fehlt oder nicht geprüft werden kann.
Wo ich durchlasse
Bei Rate-Limits. Wenn der Zähler nicht erreichbar ist, lasse ich durch. Der Zweck einer Begrenzung ist Schutz vor Überlast — daraus einen Ausfall zu machen, dreht die Wirkung um.
Bei Zwischenspeichern. Kein Zwischenspeicher heißt langsamer, nicht kaputt.
Bei Statistiken und Protokollen. Wenn das Mitschreiben scheitert, läuft die eigentliche Aktion trotzdem. Ein fehlender Protokolleintrag ist ärgerlich, ein verweigerter Befehl wegen eines Protokollfehlers absurd.
Bei Zusatzinformationen. Ein Avatar, der nicht lädt, ein optionaler Wert von einem externen Dienst — Platzhalter statt Fehler.
Die Regel dahinter
Nach ein paar Jahren habe ich eine Formulierung gefunden, die für mich funktioniert:
Verweigere, wenn Durchlassen einen Schaden anrichten kann. Lass durch, wenn Verweigern selbst der Schaden ist.
Der Test dazu ist eine einzige Frage: Was ist der schlimmste Fall, wenn ich mich falsch entscheide?
Bei Authentifizierung ist der schlimmste Fall bei „durchlassen" ein unbefugter Zugriff. Bei „verweigern" ein verärgerter Nutzer. Die Antwort ist eindeutig.
Bei Rate-Limits ist der schlimmste Fall bei „durchlassen" ein bisschen mehr Last. Bei „verweigern" ein Totalausfall für alle, ausgelöst durch einen Zwischenspeicher, der nichts mit der eigentlichen Funktion zu tun hat. Ebenfalls eindeutig, nur andersherum.
Infobox
Der Fall, bei dem ich am längsten gebraucht habe, waren Lizenzprüfungen. Wenn der Lizenzserver nicht erreichbar ist — verweigern oder durchlassen? Verweigern heißt: Mein Ausfall macht die Software bei allen Kunden kaputt. Durchlassen heißt: Wer die Verbindung kappt, umgeht die Prüfung. Meine Lösung ist ein Mittelweg mit Karenzzeit: Die letzte gültige Antwort trägt eine begrenzte Zeit weiter, danach wird verweigert. Damit überlebt man einen Ausfall von Stunden, aber keine dauerhafte Trennung.
Was man beim Verweigern beachten muss
Verweigern ist nur dann eine gute Antwort, wenn drei Dinge dazugehören.
Eine verständliche Meldung. Nicht „Fehler", sondern was passiert ist und was man tun kann. Am besten mit einer Kennung, über die man den Vorgang im Protokoll wiederfindet.
Eine Meldung an mich. Wenn eine Prüfung fehlschlägt, ist das ein Ereignis, das ich sehen will. Verweigern ohne Alarm bedeutet, dass ein defektes System still alle aussperrt.
Ein Weg zurück. Wenn ich mich aus dem eigenen System aussperren kann, ist fail-closed ein Risiko. Bei mir gibt es deshalb einen Zugang über die Maschine selbst, unabhängig von der Anwendung.
Der letzte Punkt klingt nach einem Widerspruch und ist keiner. Er ist die Anerkennung dessen, dass eine strenge Regel einen Notausgang braucht — der aber physischen Zugang verlangt und nicht über das Netz erreichbar ist.
Der Fall, der mich diese Regel gelehrt hat
Es gab einen konkreten Anlass für diese Haltung, und er war unangenehm.
Ich hatte eine Prüfung, die eine externe Angabe brauchte. Der Aufruf dorthin war in einer Fehlerbehandlung eingepackt, und der Zweig für den Fehlerfall machte einfach weiter — mit dem Ergebnis, dass die Prüfung als bestanden galt.
Der Aufruf schlug selten fehl. Aber jedes Mal, wenn er fehlschlug, wurde etwas durchgelassen, das nicht hätte durchgehen dürfen. Und weil es keinen Protokolleintrag gab, war das nicht feststellbar — weder im Nachhinein noch im Moment.
Gefunden habe ich es nicht durch einen Vorfall, sondern beim Lesen. Das ist der beunruhigende Teil: Diese Art Fehler produziert keine Symptome. Er macht ein System stiller unzuverlässig, ohne dass jemand etwas bemerkt.
Die Prüfung, die ich seitdem mache
Für jeden Codeblock, der eine Entscheidung über Zugriff trifft, gehe ich drei Fragen durch:
Welche Ausgänge hat dieser Block? Erlaubt, verweigert, Fehler. Sind alle drei ausdrücklich behandelt?
Was passiert im Fehlerzweig? Wenn dort weitergemacht wird, ist das eine bewusste Entscheidung mit Kommentar — oder ein Fehler.
Wird der Fehlerfall protokolliert? Eine Prüfung, die im Fehlerfall verweigert und schweigt, sieht für Nutzer aus wie eine Sperre ohne Grund. Sie muss laut sein.
Die dritte Frage ist die, die mir am meisten gebracht hat. Ein System, das im Zweifel verweigert und dabei meldet, warum, ist bedienbar. Eines, das im Zweifel verweigert und schweigt, produziert Ratlosigkeit — bei Nutzern und beim eigenen Support.
Warum ich das für wichtiger halte als einzelne Sicherheitsmaßnahmen
Die meisten Sicherheitsprobleme, die ich in meinem eigenen Code gefunden habe, waren keine falschen Verschlüsselungen oder fehlerhaften Prüfungen. Es waren Stellen, an denen bei einem unerwarteten Zustand einfach weitergemacht wurde.
Eine Prüfung, die im Fehlerfall stillschweigend „ja" sagt, ist gefährlicher als gar keine Prüfung — weil sie den Eindruck erweckt, es sei etwas abgesichert.