ich möchte gerne ein eigenes Loginsystem für ein Projekt umsetzen und habe mich an den Sicherheitstechniken des phpBB orientiert. Dabei viel mir auf, dass im Endeffekt doch "nur" die IP-Adresse und die nutzbaren Zeichen der Session-ID überprüft wird. Daher entschloss ich mich, ein paar weitere Punkte umzusetzen:
Code: Alles auswählen
session_start();
function encode_ip($dotquad_ip)
{
$ip_sep = explode('.', $dotquad_ip);
return sprintf('%02x%02x%02x%02x', $ip_sep[0], $ip_sep[1], $ip_sep[2], $ip_sep[3]);
}
function decode_ip($int_ip)
{
$hexipbang = explode('.', chunk_split($int_ip, 2, '.'));
return hexdec($hexipbang[0]). '.' . hexdec($hexipbang[1]) . '.' . hexdec($hexipbang[2]) . '.' . hexdec($hexipbang[3]);
}
$login = false;
$uip = ( !empty($HTTP_SERVER_VARS['REMOTE_ADDR']) ) ? $HTTP_SERVER_VARS['REMOTE_ADDR'] : ( ( !empty($HTTP_ENV_VARS['REMOTE_ADDR']) ) ? $HTTP_ENV_VARS['REMOTE_ADDR'] : getenv('REMOTE_ADDR') );
// Daten vom Formular
if ( isset($HTTP_POST_VARS['email']) && isset($HTTP_POST_VARS['pw']) )
{
if ( !empty($HTTP_POST_VARS['email']) && !empty($HTTP_POST_VARS['pw']) && !empty($uip) )
{
$sql = 'SELECT uid
FROM users
WHERE email = \'' . addslashes(trim($HTTP_POST_VARS['email'])) . '\'
AND pw = \'' . md5(addslashes(trim($HTTP_POST_VARS['pw']))) . '\'
AND active = 1';
if ( !($result = mysql_query($sql)) )
{
die('Fehler in Datei: ' . __FILE__ . '<br />Zeile: ' . __LINE__ . '<br />Fehlermeldung: ' . mysql_error() . '<br />Abfrage: ' . $sql);
}
if ( $row = @mysql_fetch_row($result) )
{
$_SESSION['uid'] = intval($row[0]);
$_SESSION['uip'] = encode_ip($uip);
$_SESSION['pw'] = md5(addslashes(trim($HTTP_POST_VARS['pw'])));
$_COOKIE['PHPSESSID'] = session_id();
}
@mysql_free_result($result);
}
}
// ohne Cookie läuft hier nichts!
if ( !empty($_COOKIE) && isset($_COOKIE['PHPSESSID']) && preg_match('/^[A-Za-z0-9]{32}$/', $_COOKIE['PHPSESSID']) )
{
// ist die Session ID mit der aus dem Cookie identisch?
if ( session_id() == $_COOKIE['PHPSESSID'] )
{
// alle Session-Daten vollständig?
if ( $_SESSION['uid'] && $_SESSION['uip'] && $_SESSION['pw'] )
{
// User ID besteht nur aus Zahlen?
if ( is_int($_SESSION['uid']) )
{
// Passwort ist entsprechend dem md5-Standard konform codiert?
if ( preg_match('/^[A-Za-z0-9]{32}$/', $_SESSION['pw']) )
{
// ohne IP-Adresse läuft hier nichts!
if ( $uip )
{
$uip = encode_ip($uip);
// Ist die IP-Adresse des Nutzers weitestgehend unverändert? (wegen AOL werden nur die ersten beiden Blöcke verifiziert)
if ( substr($_SESSION['uip'], 0, 4) == substr($uip, 0, 4) )
{
$sql = 'SELECT *
FROM users
WHERE uid = ' . $_SESSION['uid'] . '
AND pw = \'' . $_SESSION['pw'] . '\'
AND active = 1';
if ( !($result = mysql_query($sql)) )
{
die('Fehler in Datei: ' . __FILE__ . '<br />Zeile: ' . __LINE__ . '<br />Fehlermeldung: ' . mysql_error() . '<br />Abfrage: ' . $sql);
}
// Stimmen die User ID und das Passwort mit den Daten in der Datenbank überein und ist das Benutzerkonto aktiv?
if ( $user = @mysql_fetch_array($result, MYSQL_ASSOC) )
{
// Login geht in Ordnung! Die Benutzerdaten wurden erfolgreich ausgelesen.
$login = true;
}
@mysql_free_result($result);
}
}
}
}
}
}
}
// aus Sicherheitsgründen wird die Session ID gelöscht, falls auch nur eine der Verifizierungen fehlgeschlagen ist
if ( !$login )
{
session_destroy();
header('Location: ' . $host . '?p=login');
exit;
}
// ab hier geschützte Inhalte anzeigen
- ein Cookie muss vorhanden sein
- das Cookie muss eine PHP Session im richtigen Format enthalten (md5 verschlüsselt, aus Buchstaben und Zahlen bestehen und insgesamt 32 Zeichen lang sein)
- die Session ID muss mit der aus dem Cookie übereinstimmen
- die Session muss die Daten "User-ID, User-IP und Passwort" enthalten
- das Passwort muss im richtigen Format vorliegen (md5 verschlüsselt, aus Buchstaben und Zahlen bestehen und insgesamt 32 Zeichen lang sein)
- der Nutzer muss eine IP-Adresse haben
- die IP des Nutzers muss mit der aus der Session weitestgehend übereinstimmen. D.h. die ersten sechs Zahlen von insgesamt neun müssen gleich sein: 255.255.255. Eine härtere Prüfung würde dazu führen, dass AOL Nutzer ständig ausgeloggt werden.
- die Daten aus der Session müssen mit denen aus der Datenbank übereinstimmen (User-ID und Passwort)
- der Nutzer muss ein aktives Benutzerkonto besitzen
- sollte auch nur ein Versuch eine Session zu verifizieren fehlschlagen, so wird diese komplett gelöscht
Das einzige was mich noch "nervt" ist, dass beim ersten Besuch der Seite alle Links mit der Session-ID ausgestattet werden. Ich könnte auf der Zielseite einfach noch eine zusätzliche Weiterleitung einbauen um das zu vermeiden, aber das ist ja keine wirklich sinnvolle Lösung.
Fragen:
- Gibt es eine Option mit der ich die Ausgabe der PHPSESSID gänzlich verhindern kann?
- Welche Sicherheitsabfragen würdet ihr noch hinzufügen oder weglassen?
Gruß