Omawiamy inny, też ciekawy przypadek klasy napisanej jako token CSRF. Mamy tutaj ciekawe podejście i kilka technik do zapamiętania, jak fingerprinting na przykład.
Na początku metoda generująca sam token (jego tokeniczną część, że tak powiem):
<?php
class CSRF {
public static $session = '_CSRF';
//(...)
protected static function token() {
mt_srand((double) microtime() * 10000);
$charid = strtoupper(md5(uniqid(rand(), TRUE)));
return substr($charid, 0, 8) . substr($charid, 8, 4) . substr($charid, 12, 4) . substr($charid, 16, 4) . substr($charid, 20, 12);
}
//(...)
}
Ok, metoda robiąca fingerprint adresu IP oraz napisu identyfikującego przeglądarkę, zahaszowana, aby nie być całkowicie transparentną, ale też bez przesady z tym haszem, to nie hasło:
<?php
class CSRF {
public static $session = '_CSRF';
//(...)
protected static function fingerprint() {
return strtoupper(md5(implode('|', array($_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT']))));
}
}
Metoda generate, wykorzystująca zarówno „tokeniczną” jak i „identyfikacyjną” część, w dodatku pozwalająca utworzyć token tylko dla konkretnego formularza:
<?php
class CSRF {
public static $session = '_CSRF';
static function generate($form = NULL) {
$token = CSRF::token() . CSRF::fingerprint();
$_SESSION[CSRF::$session . '_' . $form] = $token;
return $token;
}
//(...)
}
Metoda check, do sprawdzania poprawności tokena:
<?php
class CSRF {
public static $session = '_CSRF';
//(...)
public static function check($token, $form = NULL) {
if (isset($_SESSION[CSRF::$session . '_' . $form]) && $_SESSION[CSRF::$session . '_' . $form] == $token) { // token OK
return (substr($token, -32) == CSRF::fingerprint()); // fingerprint OK?
}
return FALSE;
}
//(...)
}
Swoją drogą, choć wydaje się, że autor wyszedł z siebie, aby o wszystkim pomyśleć, o jednym zapomniał – po użyciu tokena z sukcesem powinno nastąpić jego usunięcie, unset na tokenie (w $_SESSION).
Niedługo jeszcze lepsze metody poznamy.