Checkliste für Websicherheit

Inhalt
- XSS (Cross Site Scripting)
- SQL-Injection
- Benutzereingaben
- CSRF (Cross Site Request Forgerey)
- Dateiuploads
- Komplexe Berechnugen
- Geheist werden
- Header Injection
- Session Fixation
- Security through Obscurity
- Umgang mit Passwörtern
Um unseren kleinen Beitrag zu einer sicheren Welt zu leisten wollen wir nicht nur zum Welt AIDS Tag Kondome über Outdoor-Kunstwerke an Bahnhöfen stülpen, sondern diesmal auch etwas eher praktisch Orientiertes anbieten. Im Folgenden sehen wir uns eine Liste möglicher Angriffe auf Webanwendungen an. Es geht dabei nicht zwangsweise um PHP, auch wenn die Beispiele aufgrund unserer Firmennatur PHP-Code sein werden. Außerdem geben wir uns nicht mit einer popeligen Gliederung ab, da die meisten Probleme eh vielfältig sind und Schubladen nicht mögen.
Ich werde wenig bis gar nicht auf praktische Angriffe eingehen, da ich davon ausgehe, dass jeder entweder Googlen bemühen kann oder bereits weiß, wie XSS & Co. funktionieren.
XSS (Cross Site Scripting)
Werden alle Ausgaben, die in irgendeiner Form vom Benutzer abhängen, entsprechend dem Ausgabemedium kodiert?
Das heißt, werden bei HTML alle Variablen durch htmlspecialchars() verarbeitet? Dabei nicht vergessen: Encoding richtig setzen! Andernfalls knallt's bei Multibyte-Ausgaben ganz schnell.
- Böse:
print $_GET['page']; - Besser:
print htmlspecialchars($_GET['page']); - Noch besser:
print abs(intval($_GET['page'])); - Ideal (ab PHP 5.3):
print abs(intval(isset($_GET['page']) ?: 1));
(Wir gehen davon aus, dass in page vermutlich die aktuelle Seite einer Auflistung enthalten ist. Und, dass dieser Wert nicht negativ sein sollte.
Benutzerspezifische Werte können aus vielen Quellen kommen:
- GET und POST (die üblichen Verdächtigen)
- Cookies (auch die Session-ID, die dort oft liegt, kann manipuliert sein)
- in seltenen Fällern auch HTTP-Header (auch wenn diese eher selten PHP-seitig ausgewertet werden)
- hochgeladene Dateien (den Angaben in
$_FILESist auch nicht zu trauen)
Hier gilt: Immer so spät wie möglich escapen. HTML-kodierte Werte bringen in der normalen Applikation rein gar nichts. Auf keinen Fall sollte htmlspecialchars() irgendwo im Model auftauchen, in der Regel auch nicht im Controller.
Spätes Escapen ergibt nicht nur mehr Sinn, sondern sorgt auch dafür, dass das Ausgabemedium beliebig gewechselt werden kann. Statt HTML könnte LaTeX die Ausgabe sein, bei der dann natürlich kein htmlspecialchars() mehr zum Einsatz kommen muss (dafür dann eine texspecialchars()).
SQL-Injection
(Ich komme zu dem Thema, weil sich hier die logische Fortsetzung der Ausgabekodierung gut anschließen lässt.)
Werden alle Werte, die in irgendeiner Form in Queries verwendet werden, entsprechend dem DBMS und ihrem Datentyp kodiert?
Das alte Problem von SQL-Injections dürfte so ziemlich jedem bekannt sein:
$qry = "SELECT * FROM mytable WHERE key = $_GET[foo]";Hier müssten wir
- entweder davon ausgehen, dass
keynumerisch sein soll. In diesem Fall fehlt ein intval() oder ähnliches. - oder davon ausgehen, dass
keyein String ist. Dann fehlen sowohl die Quotes im SQL (WHERE key = '$_GET[foo]') als auch das Escaping eben jener in der Variable.
Gerade das Escapen kann jedoch tricky sein: Wie schon beim Escapen der Ausgabe muss hier der Zeichensatz beachtet werden. addslashes() kennt die verschiedenen SQL-Engines nicht und wird daher die spezifischen Steuerzeichen ebenso unbeachtet lassen wie Multibyte-Strings. Im Falle von MySQL ist also mysql_real_escape_string() die Funktion der Wahl.
Doch auch hier gilt Vorsicht: Ändert man den Zeichensatz der Verbindung nur über MySQL (mysql_query("SET NAMES 'utf8'")), so kriegt mysql_real_escape_string() von dieser Änderung nichts mit. Hier müsste man also stattdessen die native mysql_set_charset()-Funktion bemühen, sonst kann es bei UTF-8-Verbindungen auch zu Problemen kommen.
Im Idealfall abstrahiert man den Datenbank-Zugriff bei größeren Projekten gänzlich über einen OR-Mapper (Doctrine, Propel, …) weg oder nutzt für kleinere Projekte zumindest PDO und die dort verügbaren Prepared Statements, womit das lästige Escapen gänzlich unter den Tisch fällt.
Ein abschließender Hinweis dazu noch: Ein eventuell von PHP durchgeführtes Magic Quoting sollte in jedem Fall von der Anwendung erkannt und entfernt werden. Einerseits machen escapte Werte beim normalen Verarbeiten (ohne SQL) nur Probleme. Andererseits sind die Werte nicht richtig escaped, um in SQL verwendet zu werden (siehe Multibyte-Encodings). Die Anwendung sollte das Escaping immer so spät wie möglich vornehmen, bestenfalls beim Zusammensetzen der Anfrage. Auf diese Weise vermeidet man, dass manche Variablen escaped vorliegen und manche nicht. Das Escapen ist damit meistens Aufgabe des Models (in MVC-Architekturen).
Benutzereingaben
All users are either stupid, evil or both. And so is their input.
Das alte Spiel noch einmal zusammengefasst:
- Egal, was der Benutzer eingibt, es ist vermutlich falsch.
- Er weiß es nicht besser.
- Er hat sich vertippt.
- Er hat vorsätzlich etwas Böses eingegeben.
- Validiere jeden Wert, egal, wie trivial er erscheint:
- Seitennummer in der URL? Das muss eine Zahl sein. Und die muss sogar meistens positiv sein:
abs(intval()) - Zeichenketten allgemein: Keine eMail ist 65k Zeichen lang, kein Forenbeiträge 3 MB:
trim(substr($string, 0, $maxlen))- Achtung: nur auf nicht-escapte Strings anzuwenden, da sonst Entities (HTML) oder Slashes (SQL) abgeschnitten werden können!
- Benutzername: Einzeiliger String, i.d.R. recht eingeschränkt:
preg_replace(whitelist, "", $username) - Datumsangaben: Geburtstag des aktuellen Benutzers war erst vor einem Jahr? Oder vor 1900? Ablehnen (nicht: korrigieren). Parsing beispielsweise via
strtotime()(Vorteil: Benutzer kann Datum vermutlich in jeder ihm bekannten Weise eingeben.)- Entsprechende Prüfroutinen sollten dynamisch sein (Maximaljahr nicht fest
$X, sonderndate('Y') - $X) - Das macht sich dann auch gut, wenn man eine Selectbox mit Jahresangaben befüllen will. Es sei denn, man will den Kunden dazu bringen, jedes Jahr auf's neue das Hinzufügen eines Jahres zu einer Box zu beauftragen. ;-)
- Entsprechende Prüfroutinen sollten dynamisch sein (Maximaljahr nicht fest
- Blacklists sind niemals vollständig. Das Prüfen, ob eine Eingabe auf einer Blacklist steht kann daher nur schiefgehen.
- Whitelists sind daher in jedem Fall vorzuziehen.
- Benutzereingaben haben nur sehr wenig und nur nach ausreichender Prüfung (Whitelist) in Funktionen wie
include,fopen,file_get_contents, ... zu suchen. - Nullbytes fungieren in PHP innerhalb von Strings wie ein Kommentarzeichen:
include("$foo.html");$foo = "../../../include/config.php\0";- ⇒
include("../../../include.config.php");
- Seitennummer in der URL? Das muss eine Zahl sein. Und die muss sogar meistens positiv sein:
CSRF (Cross Site Request Forgerey)
(Eigentlich sollte man das in Konsistenz zu XSS auch mit XSRF abkürzen.)
Werden alle schreibenden Aktionen (Löschen, Anlegen, Verschieben, Umbennnen) per POST-Anfrage ausgeführt?
In einer idealen Welt würden wir wohl neben POST auch noch PUT und DELETE als HTTP-Methoden nutzen. Aber die Welt ist nicht ideal. Und POST reicht meistens auch vollkommen aus.
Die Wichtigkeit, schreibende Aktionen nur über POST zugänglich zu machen, zeigt sich, wenn man CSRF-Attacken näher untersucht. Ein Großteil der Angriffe wären nicht möglich gewesen, hätte die Anwendung auf POST gesetzt.
Dazu reicht es nicht, nur das eigene Formular auf POST zu ändern. Auch die Anwendung muss in derartigen Fällen explizit über $_SERVER['REQUEST_METHOD'] prüfen, ob es sich um POST handelt. Schöner wäre es noch, wenn man ein explizites CSRF-Token einführt, was aber in den meisten Fällen die Benutzerfreundlichkeit gewaltig drücken kann. Beispielcode könnte wie folgt aussehen:
function ensure_method($method)
{
if (strtoupper($method) !== $_SERVER['REQUEST_METHOD']) {
die('Request must be made using '.strtoupper($method));
}
}
ensure_method('POST');Dateiuploads
Sind Dateiuploads in Größe und Anzahl beschränkt?
Dateiuploads machen mir persönlich immer wieder Bauchschmerzen:
- Erlaube ich zu große Dateien, ist mein Webspace irgendwann voll.
- Erlaube ich nur kleine, sind meine Benutzer unzufrieden.
- Wie viele Dateien soll ich pro Benutzer in einer bestimmten Zeit zulassen?
- Wo speichere ich die Dateien?
- Worauf kann ich mich verlassen? Dateinamen? Dateitypen?
Irgendwie wirken Dateiuploads wie große, klaffende Wunden in der Anwendung. Jeder Besucher sieht sie, jeder kann etwas reintun und wenn's schmutzig war, stirbt die Anwendung. Kein schöner Zustand und auch ein wenig paradox, wenn wir alles andere mit Firewalls abriegeln.
Wichtige Leitsätze sollten hierbei sein:
- Wähle die Dateigröße dem Zweck angemessen. Niemand muss für sein Profilfoto ein 10 MB JPEG hochladen dürfen.
- Vertraue nicht darauf, dass die Datei einen sinnvollen Namen hat(te). Generiere selbst einen, beispielweise aus der User-ID (534.jpg), dem Benutzernamen (wenn dieser z.B. per Regex abgesichert ist) oder erzeuge einen Hash. Du willst definitiv nicht den Namen übernehmen, den dir der Benutzer geschickt hat.
- Schließe nie von der Dateiendung auf den Dateityp. Wenn möglich, vermeide es gänzlich, auf den Typ zu vertrauen und behandle alle Dateien als potentiell böse Binärdateien oder potentiell böse Scripts.
- Sei extrem vorsichtig, wenn du Archive erlaubst und diese entpackst.
- Directory Traversal (oh, mein Zip enthält eine Datei namens "../../../../../etc/passwd") ist gefährlich. Im schlimmsten Fall überschreibt jemand die komplette Anwendung mit seiner eigenen Version.
- Ein 100 KB großes Ziparchiv kann auch einfach 100 MB Daten ("aaaaaa…") enthalten.
- Du willst niemals hochgeladene Dateien einbinden (include, require, eval(file_get_contents())). Egal für welchen Zweck.
- Beachte, wer welche Dateien sehen darf. Sollen sie nur für den Uploader zugänglich sein?
- Stelle sicher, dass jedem Benutzer ein Quota zukommt. Oder wenigstens ein systemweites Quota. Du willst nicht, dass dir ein Scriptkiddie deinen Webspace mit Pornobildchen zupflastert und/oder dich als seinen persönlichen Dateihoster missbraucht.
- Müssen die Dateien von außen zugänglich sein?
- wenn nein: per htaccess schützen oder außerhalb des Webroots ablegen
- wenn ja: umbenennen und ggf. in dem Verzeichnis sämtliche dynamischen Features des Webservers ausschalten (du willst nicht, dass dir jemand ein PHP-Script nach /uploads/ schiebt und es in seinem Browser aufrufen und ausführen kann!). Du kannst die Dateien immer über ein extra Script an den Client senden, dass den Dateiinhalt einliest (
readfile()eignet sich hervorragend dazu).
Wenn viele (wirklich viele) Metadaten zu einer Datei benötigt werden und diese auch relativ klein sind, kann man auch darüber nachdenken, sie in der Datenbank (als BLOB) abzulegen. Bilder und größere Dateien würde ich davon jedoch immer ausnehmen, da man mit BLOBs und CLOBs sämtliche Stärken von SQL (Suchen, Selektieren, Verbinden, …) wegwirft (dann kann man auch CouchDB oder das Dateisystem nehmen).
Ein Beispiel soll noch verdeutlichen, wie gefährlich es sein kann, auch vermeintlich sichere Dateien einzubinden. Nehmen wir an, ein Benutzer lädt ein beliebiges Bild auf den Server. Mittels ImageMagick oder GD oder GIMP haben wir sichergestellt (indem wir versucht haben, es zu öffnen, was uns gelang), dass es sich wirklich um ein JPEG-Bild handelt. Theoretisch können wir es also einbinden.
// send.php
// Forbid direct access to a file and instead send it via this script so that
// we can add caching header and stuff.
include("$_GET[file].jpg");Ganz davon abgesehen, dass dieser Code ohnehin mehrere Schwachstellen aufweist, nehmen wir ihn mal hin. Der Besucher sieht nun die folgende Ausgabe:
Warning: Unterminated comment starting line 1 in D:\test.jpg on line 1
Ï Ó ►JFIF ☺☺☺ ` ` ■ %Hello from JPEG!Was ist passiert? Nun, ein Blick in das Bild offenbart das Problem:

Ich habe vor dem Upload als JPEG-Kommentar die Programmzeile
<?php print "Hello from JPEG!";/*in mein Bild eingebettet. Und dabei war ich noch nett. Ich hätte auch den Webspace leeren oder dergleichen machen können. Und das auch ohne das Anzeigen von Fehlern. Wir lernen also, dass auch eigentlich geprüfte und für gut befundene Daten ihre Tücken haben können (und dass das Einbinden von hochgeladenen Dateien eine wirklich ganz schlechte Idee ist).
Komplexe Berechnugen
Kann mein Besucher über den Aufruf einer URL eine rechenintensive Aktion anstoßen?
Nehmen wir mal an, wir bieten einen Remote-Download-Service an. Besucher geben uns URLs, die sie aus irgendeinem Grund nicht aufrufen können, und wir laden sie für sie auf unseren Server (dabei gilt das eben schon für Dateiuploads Gesagte was identisch) und sie können sie später herunterladen. Wenn wir diesen Upload direkt auf Benutzerwunsch durchführen, ist nach 5 Besuchern (oder einem sehr bösen Besucher) unsere Bandbreite dicht. Das Gleiche gilt für komplexe Datenbank-Queries oder sonstige Berechnungen. Cachen oder Ablehnen. Wir wollen auch nicht, dass unsere Besucher mit jedem Seitenaufruf das Abrufen externer Quellen anstoßen können (z.B. soll ein Kunde in einem Shop nicht durch einen Klick auf einen Button eine Tracking-Anfrage an UPS starten dürfen).
Im Allgemeinen ist es hier sinnvoll, derartige Aufgaben vom Benutzer abzukoppeln. Zu holende Dateien (Remote-Download-Beispiel) können in einer Warteschlange landen. UPS-Trackings können per Cronjob unabhängig vom Geschehen auf der Webseite durchgeführt werden.
Im Idealfall kann ein Besucher durch seine Aktionen keine aufwändigen Operationen auf dem Server anstoßen.
Geheist werden
Läuft meine Anwendung auch noch dann [performant], wenn ich geheist / geslashdottet / populär werde?
Hier spielt die Sicherheit nur eine untergeordnete Rolle. Dennoch möchte ich das Thema gern ansprechen, da viele Seiten das Problem ignorieren. Beim Entwickeln der Anwendung sollte immer die Zielgruppe im Auge behalten werden: Facebook wird sicherlich keine Gesamtliste aller Mitglieder im Backend erzeugen während der kleine Tante Emma Laden um die Ecke seine Webseite sicher nicht redundant über 6 Server splitten wird. Augenmaß ist das Stichwort.
Einige Probleme, die auftreten können, wenn die Webseite und die in ihr enthaltenen Datenberge wachsen:
- Erzeugen eine SQL-Queries Full-Table-Scans? Das mag bei 10 Benutzern nicht wehtun, bei 10 Millionen wird MySQL schwer zu tun haben.
IndizesIndexe helfen. - Erzeuge ich irgendwo Listen von Datenbankeinträgen (Gästebuch, Benutzerliste, Forenbeiträge, …)? Was passiert, wenn mein Gästebuch zugespamt oder sehr populär wird? Die Datenbank schaufelt bergeweise Daten in mein Script, die erzeugten Seiten werden mehrere MB groß. Hier helfen Pager (Seite 1, 2, …) oder Selectboxen (auch wenn Selectboxen sich nicht so gut skalieren lassen).
- Was passiert, wenn mein Gästebuch richtig populär wird? Dann erzeuge ich Pager mit Links zu Zehntausenden Seiten. Hier hilft es, nur die erste, letzte sowie die 3 Seiten um die aktuelle herum auszugeben (Seite 1 … 3 4 5 6 7 … 10000000).
- Habe ich einen Cache in meiner Anwendung? Sind meine Assets (JS/CSS/Bilder) clientseitig cachebar?
- Verschicke ich basierend auf Benutzeraktionen (Formular abgeschickt ohne Captcha, …) eMails? Was passiert, wenn mein Gästebuch populär wird? Will ich Hunderte Mails erhalten, bis mein eMail-Anbieter die IP meines Hosters blockiert?
- Dummerweise ist das Beschränken von Mails so eine Sache. Man muss irgendwo Buch darüber führen, wie viele Mails wann rausgegangen sind.
- Wenn ich viel Traffic habe, was passiert mit meinen Datenbankzugriffen? Hier helfen Transaktionen oder Table Locks, Chaos zu verhindern (und verbessern teilweise auch noch die Performance).
- ...
Nicht alle Probleme treffen auf alle Projekte zu, aber bei der Entwicklung sollte man derartige Probleme immer im Hinterkopf haben. Und sicherlich sind nicht alle Techniken immer bis ins Unendliche skalierbar und eine Neu-Implementierung wird notwendig.
Header Injection
Verwende ich Benutzerdaten in HTTP/eMail-Headern?
Das Problem mit HTTP-Headern ist nicht so dramatisch, da neuere Versionen von PHP in header() von Haus aus keine Zeilenumbrüche zulassen. Theoretisch kann aber eine böse Benutzereingabe dazu führen, dass in header() mehrere HTTP-Header erzeugt werden, was alle Arten von seltsamen Verhalten (Stichwort: Response Splitting) zur Folge haben kann. Hier gilt es, den Raum erlaubter Werte minimal zu halten ([a-z0-9.-]+ oder dergleichen).
Viel dramatischer sind hingegen Header in eMails. Ein typisches Script sieht wie folgt aus:
// Tell-a-friend-Script
$to = $_POST['to'];
$from = $_POST['from'];
$subject = "Hey, check this out!";
$body = "Hey, check out this wunderful website over there!";
mail($to, $subject, $body, "From: $from", "-f$from");Ganz davon abgesehen, dass das Weiterreichen der Parameter an sendmail ohne weitere Prüfung schon fast einer Shell Injection gleicht, ist das Setzen des From-Headers sehr dramatisch. Nicht selten heißen die Empfänger solcher eMails plötlich
Timmy <timmy@foo.com>\nTo: spamopfer1@isp.com\nTo: foo@bar.com\nBCC: Foo\nSubject: Viagra!Selbst wenn man aufgrund der Natur von eMail-Adressen diese nur schwer richtig validieren kann (kleine Regex können gefährliche Lücken haben oder viel zu restriktiv sein), kann und sollte man immer Newlines und derartige Sonderzeichen aus eMail-Adressen entfernen.
Session Fixation
Bekommt ein Benutzer beim Login eine neue Session-ID?
Session Fixation läuft in etwa wie folgt ab:
- Eve bringt Alice dazu, eine Webseite X aufzurufen. Dabei gibt Eve Alice bereits ein Cookie mit, in dem eine Eve bekannte Session-ID steht.
- Alice loggt sich auf der Webseite ein. Sie erhält keine neue Session-ID, sondern die beim ersten Aufruf gefundene wird nur mit einem Flag „eingeloggt“ versehen (oder anderweitig als authorisiert gekennzeichnet).
- Eve kann sich selbst ebenfalls ein Cookie geben, dass die ihr bekannte Session-ID enthält.
- Eve ruft Webseite X auf. Wegen der identischen SID ist Eve ebenfalls eingeloggt.
Das Beispiel zeigt, dass es notwendig ist, Alice beim Login ein neues Cookie mit einer neuen SID zu geben. Nur dann ist die alte SID von Eve nicht mehr gültig. Das verhindert natürlich nicht, dass Eve Alice das Cookie nicht anderweitig (XSS, ...) stiehlt.
Leitsatz: Wenn sich das Benutzerlevel ändert, generiere eine neue Session-ID.
Security through Obscurity
Gibt es Funktionen auf meiner Seite, die nur dadurch geschützt sind, dass ihr Ort (hoffentlich) einer nicht authorisierten Person nicht bekannt ist?
So verlockend es manchmal auch sein kann, man sollte sich niemals auf derartige „Sicherheit“ verlassen. Da es sich dabei nicht um wirkliche Sicherheit handelt, ist eigentlich schon die Überschrift falsch. Es ähnelt mehr Russischem Roulett mit den Komponenten des Systems.
Vielleicht war der Entwickler so clever, das Godlike-Script „45z3i5u24h2g2h.php“ zu nennen, in der Annahme, dass nur jemand, der diesen Namen kennt, es auch aufrufen kann (und überhaupt erst danach sucht). Dumm nur, dass der Betreiber der Seite aus Versehen seine index.html gelöscht hat und Apache ein Directory Listing erzeugt. Fail.
Okay, das war konstruiert. Warum sollte der Betreiber seine index.html löschen? Okay, die Datei ist da. Mist, in Super-Duper-CMS Version 1.0.9 ist ein Bug: Beim Uploadmanager für Besucher ist eine Directory Traversal-Lücke. Statt /media/ kann ein Angreifer jedes Verzeichnis auflisten lassen. Auch / – und da liegt unser Godlike-Script. Fail.
Wir sehen, die Möglichkeiten sind vielfältig. Der Fehler, der das Kartenhaus des Versteckens in sich zusammenfallen lässt muss nicht einmal bei uns liegen. Selbst wenn wir wissen, dass wir Fehler machen: Wie unrealistisch ist es, weder Fehler in anderen Komponenten noch menschlisches Versagen anzunehmen?
Umgang mit Passwörtern
Speichere ich Passwörter im Klartext?
Der Umgang mit Passwörtern sollte zur Genüge bekannt sein:
- Speichere Passwörter niemals im Klartext.
- Verwende eine kryptografisch sichere Hashfunktion (SHA-Familie)
- Verwende immer einen (dynamischen oder statischen) Salt:
- statisch:
$hash = sha1($userPassword.'thisismyrandomsaltthatnoonecaneverguess') - dynamisch:
$hash = sha1($userPassword.$userID)– Dynamische Salts sind Werte, die pro Benutzer verschieden aber dennoch konstant sind (User-ID, Registrierzeitpunkt, ggf. Benutzername oder eMail). - Vorteile:
- Wir sichern uns gegen Rainbow-Tables ab: Bei einem statischen Hash muss ein Angreifer sämtliche möglichen Passwörter 1x neu hashen, um eine Datenbank aller Passwörter zu haben. Beim dynamischen Salt muss ein Angreifer dies für jedes einzelne Passwort tun.
- Die Benutzer sind untereinander besser geschützt: Selbst wenn zwei Benutzer das gleiche Passwort haben, werden beim dynamischen Salt verschiedene Hashes erzeugt.
- statisch:
- Wenn gewünscht (paranoide Kunden gibt es überall), kann man auch das Hashen mit einem linearen Zeitfaktor versehen (1000x SHA-1 dauert trotzdem nur wenige Millisekunden, aber die Masse macht's).
- Achtung: Nicht zuviel Rechenlast auf dem Server erzeugen!
- Verlange eine Mindestlänge vom Benutzer (mindestens 6 bis 8 Zeichen).
- Limitiere den Benutzer nicht zu sehr in der Wahl der Zeichen im Passwort (KeePass-Benutzer haben gern Sonderzeichen im Passwort).
- Vermeide domainspezifische Passwörter: Diese Technik kann nur unter Berücksichtigung der jeweiligen Zielgruppe angewandt werden. Auf einer Fanseite für Scrubs sollten Passwörter wie „JD“, „Doktor Cox“, „Bob Kelso“, „Hausmeister“ usw. auf einer Blacklist stehen und nicht erlaubt sein. Ggf. kann man die Begriffe auch in das Wörterbuch integrieren, das man mit cracklib nutzt, um auch noch alle Varianten dieser Namen austesten zu lassen („Bob Kelso“, „bobkelso“, „kelso“, ...).
- Wenn eine Sicherheitsfrage eingegeben werden soll, um eine Passwort-vergessen-Funktion zu implementieren, lass den Benutzer seine Frage selber wählen. Die meisten typischen Fragen (Geburtsname der Mutter oder Name des Haustiers (siehe Paris-Hilton-Hack)) sind zu leicht über Social-Engineering zu knacken.
- Wenn du deinem Benutzer ein neues Passwort zuschickst, stelle sicher, dass sein altes so lange weiter gültig ist, bis er sein neues gewählt hat. Andernfalls kann Eve für Alice einfach ein neues Passwort anfordern und Alice muss sich entweder das neue merken oder andauernd ihr altes wieder eintragen. Das nervt gewaltig. Es gibt im Prinzip zwei Verfahren, das Passwort zu ändern:
- Generiere ein Alternativpasswort und prüfe beim Login sowohl das echte als auch das alternative. Problem: Ein Hacker muss nun nur noch eines von zwei Passwörtern erraten (doppelte Chance auf Treffer).
- Leite den Passwort-ändern-Prozess erst ein, wenn der Benutzer in der eMail auf den Zurücksetzen-Link klickt. Auf diese Weise existiert immer nur ein gültiges Passwort gleichzeitig.
