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.