Zum Inhalt springen
KKrynex Labs
Alle Artikel
#sicherheit#audit#ki#webentwicklung#praxis

Fünf Befunde auf der eigenen Website — und warum wir sie erst nachgeprüft haben

Ein Audit unserer Seite fand fünf Punkte, alle berechtigt. Eine Woche zuvor fand ein anderes Audit 24 Punkte, von denen 15 standhielten. Was der Unterschied für die Arbeitsweise bedeutet.

Annett Krusch
Fünf Befunde auf der eigenen Website — und warum wir sie erst nachgeprüft haben

Was das Audit gefunden hat

Wir haben unsere eigene Website prüfen lassen. Fünf Punkte kamen zurück, und ich sage gleich vorweg: alle fünf waren berechtigt. Das ist kein Selbstlob für den Prüfer und keine Zerknirschung von uns, sondern der interessante Teil der Geschichte, weil eine Woche vorher ein anderes Audit ganz anders ausging.

Der Reihe nach.

Unser Vorschaubild für soziale Netzwerke war 1200 mal 685 Pixel groß. In den Metadaten stand 1200 mal 630. LinkedIn und Facebook verlassen sich auf diese Angabe und schneiden entsprechend zu, das Bild wird also anders dargestellt als gedacht. Wir haben das Bild beschnitten statt die Angabe zu korrigieren, denn 1200 mal 630 ist das Verhältnis, das diese Plattformen ohnehin erwarten.

Unsere Sprachverweise, die Google sagen, welche Seite die deutsche und welche die englische Fassung ist, zeigten auf Adressen ohne abschließenden Schrägstrich. Der Server leitet diese Adressen aber per 301 auf die Fassung mit Schrägstrich um. Google verlangt an dieser Stelle die Endadresse und wertet einen Verweis auf eine Weiterleitung nicht als Sprachalternative. In der Search Console taucht das als Fehler auf. Wir haben die Normalisierung nicht in die betroffenen Seiten geschrieben, sondern eine Ebene höher in die Komponente, die alle Metadaten erzeugt — damit kann es nicht wieder verrutschen, wenn jemand eine dritte Sprachfassung anlegt.

Der dritte Punkt war der, bei dem ich am längsten geschluckt habe. Im Cookie-Hinweis standen die Links zu Datenschutzerklärung und Impressum als leere Verweise im Quelltext, und ein Skript trug die Ziele beim Laden nach. Es leitete den Basispfad sogar aus einem fremden Link im Fußbereich ab. Damit hingen zwei Pflichtangaben an drei Bedingungen gleichzeitig: das Skript muss laufen, es muss vor dem Klick fertig sein, und der fremde Link muss existieren. Fällt eine davon aus, führen Datenschutz und Impressum ins Leere. Das sind ausgerechnet die beiden Links, die auch ohne JavaScript funktionieren müssen.

Viertens hatten die Eingabefelder unseres Chat-Fensters nur Platzhaltertexte statt Beschriftungen. Ein Platzhalter ist keine Beschriftung: er verschwindet, sobald jemand tippt, und Spracheingabe kann das Feld nicht ansprechen. Kurios daran ist, dass unser Kontaktformular und unser Newsletter-Feld es längst richtig machen. Das Chat-Fenster war der Ausreißer, und niemandem ist es aufgefallen, weil man den Fehler nur bemerkt, wenn man die Seite nicht so bedient wie man selbst.

Der fünfte Punkt war der einzige, der echte Arbeit gemacht hat. Von sechs üblichen Sicherheitskopfzeilen hatten wir fünf. Es fehlte die Content-Security-Policy — die Regel, die dem Browser sagt, aus welchen Quellen er Skripte laden darf. Dazu unten mehr, denn daran hängt der eigentliche Punkt dieses Textes.

Eine Woche vorher: 24 Befunde, von denen 15 standhielten

Kurz davor hatten wir ein anderes Werkzeug von uns prüfen lassen, unser Entwicklungswerkzeug OpenWolf. Auch dort kam eine ordentlich formatierte Liste zurück, 24 Punkte, sortiert nach Schwere, jeder mit Datei und Zeilennummer.

Wir haben jeden einzelnen am Code nachgelesen, bevor wir etwas geändert haben. Ergebnis: 15 hielten stand. Sieben waren in der Begründung falsch oder deutlich schwächer als behauptet. Zwei waren schlicht falsch.

Zwei Fälle sind lehrreich genug, um sie zu nennen.

Beim ersten stimmte das Ergebnis, aber die Ursache war falsch. Der Bericht sagte, eine fehlerhafte Datei bringe unsere Auswertungsoberfläche zum Absturz, weil dort ungeprüft eingelesen werde. Das Einlesen war sehr wohl geprüft. Was fehlte, war die Prüfung der Form: eine Datei kann formal korrekt sein und trotzdem das Falsche enthalten. Hätten wir die genannte Ursache repariert, wäre der Absturz geblieben.

Beim zweiten war der gemeldete Angriffsweg der harmlose. Der Bericht beschrieb eine Schnittstelle, die hinter einer Zugangsprüfung liegt — dort ist der Angreifer also derjenige, der ohnehin schon Zugang hat. Der Weg, der wirklich offen stand, kam aus einer ganz anderen Richtung und stand nicht im Bericht.

Und dann war da die Einordnung. Der schwerste Fund der ganzen Liste stand unter „niedrig”: eine Kennung aus einer mitgelieferten Konfigurationsdatei wurde ungeprüft zu einem Dateinamen. Wer die Datei kontrolliert, bestimmt damit, wohin geschrieben wird — mit den Rechten des Hintergrundprozesses, nach Zeitplan. Der als mittelschwer geführte Punkt darüber war der aus dem vorigen Absatz, der praktisch nicht ausnutzbar ist.

Wenige Tage später kam dieselbe Erfahrung noch einmal aus einer anderen Richtung. Wir gleichen unser Werkzeug regelmäßig mit dem Projekt ab, aus dem es hervorgegangen ist. Dessen neue Fassung listete 16 behobene Fehler. Wir haben wieder jeden einzelnen gegen unseren eigenen Stand gehalten: zehn trafen zu, sechs nicht — teils, weil sie Funktionen betreffen, die wir gar nicht haben, teils, weil wir es längst anders lösen.

Messen statt argumentieren

Einen dieser Punkte haben wir nicht durch Nachdenken erledigt, sondern durch Nachsehen, und das ist der Teil, den ich mir am ehesten gemerkt habe.

Der fremde Bericht behauptete, eine bestimmte Umgebungsvariable sei in unserem Umfeld nie gesetzt, weshalb alle Sitzungen falsch zugeordnet würden. Man könnte darüber lange diskutieren. Wir haben stattdessen in unsere eigenen Zahlen geschaut: 110 Sitzungen korrekt zugeordnet, eine nicht, die von heute inklusive. Damit war die Sache erledigt, ohne eine Zeile Code anzufassen.

Der Grund für den Irrtum war übrigens nachvollziehbar. Die Variable fehlt tatsächlich — nur eben an einer anderen Stelle als der, auf die es ankommt. Ein Befund kann aus einer korrekten Beobachtung entstehen und trotzdem nicht zutreffen.

Zurück zur Website: wie wir die Sicherheitsregel gebaut haben

Bleibt der fünfte Punkt, die fehlende Content-Security-Policy.

Bei einer Seite, die aus fertigen Dateien besteht und nicht bei jedem Aufruf neu erzeugt wird, kann man dem Browser kein wechselndes Einmalkennwort mitgeben. Es bleibt der Weg über Prüfsummen: jedes im Seitenquelltext stehende Skript bekommt seine Prüfsumme in die Regel. Von Hand gepflegt wäre das eine Liste, die bei jeder Änderung stillschweigend falsch wird — und eine falsche Regel dieser Art fällt nicht auf. Sie blockiert einfach etwas, das vorher lief, und zwar beim Besucher, nicht bei uns.

Also wird die Regel jetzt bei jedem Bauvorgang aus dem fertigen Ergebnis abgeleitet. Zwölf Prüfsummen decken 369 Vorkommen ab.

Dann kam die Frage, die man aus der Spezifikation herleiten könnte: Fallen die strukturierten Daten, mit denen wir Suchmaschinen unsere Inhalte beschreiben, auch unter diese Regel? Davon stehen 484 Blöcke in unseren Seiten. Wären sie betroffen, bräuchte jeder einzelne eine Prüfsumme.

Wir haben es nicht hergeleitet, sondern gemessen: die fertigen Seiten lokal mit genau dieser Kopfzeile ausgeliefert, zehn Seiten in einem echten Browser geladen, jede Beanstandung eingesammelt. Null Verstöße.

Und weil ein Test, der immer bestanden wird, gar nichts beweist, haben wir ihn geeicht. Eine Prüfsumme aus der Regel entfernt, den Test noch einmal laufen lassen — er schlug an und nannte exakt die fehlende. Erst danach war das Ergebnis etwas wert.

Denselben Maßstab haben wir an unsere eigenen Reparaturen am Werkzeug gelegt. Die Tests, die wir dafür geschrieben haben, fanden zwei Fehler in Korrekturen, die zu dem Zeitpunkt wenige Minuten alt waren. Einer davon war plattformabhängig und auf unserem eigenen Rechner unsichtbar.

Was wir daraus für Kundenprojekte mitnehmen

Ein Audit — egal ob von einem Menschen, einem Werkzeug oder einem Sprachmodell — ist eine Liste von Hypothesen, keine Liste von Befunden. Es ist trotzdem wertvoll: der Prüfer unserer Website lag fünf von fünf Mal richtig, und mehrere Punkte hätten wir von allein nicht gesehen.

Aber zwischen „steht im Bericht” und „ist in unserem Code so” liegt ein Arbeitsschritt, den man nicht überspringen kann. Bei uns kostet der pro Punkt selten mehr als ein paar Minuten Lesen. Ohne ihn repariert man im besten Fall etwas, das nicht kaputt war, und im schlechteren die falsche Ursache eines echten Fehlers.

Drei Dinge haben sich dabei bewährt. Jede Behauptung an der genannten Stelle nachlesen, bevor irgendetwas geändert wird. Die Einordnung selbst neu vergeben, statt die Reihenfolge des Berichts zu übernehmen. Und dort, wo eine Behauptung messbar ist, messen statt argumentieren.

Wer selbst gerade so eine Liste auf dem Tisch hat und unsicher ist, welche Punkte davon tatsächlich das eigene System betreffen: genau diese Gegenprüfung machen wir auch für Kundensysteme, und wir rechnen sie an, wenn daraus ein Projekt wird.

Passend dazu