Snadná práce s tokeny
Říkal jsem si, že když už několik let vlastně pořádně neprogramuji, že je škoda nechat některé moje knihovny ladem. Že by se třeba mohly někomu hodit. Vytvořil jsem tedy na tomto webu sekci Download, kde to všechno je. Užijte to dle libosti. Tedy všechno… Zatím tam je jenom jedna věc, další možná přibudou časem. A tou jednou věcí je třída pro práci s tokeny.
Tokeny ve smyslu tohoto článku jsou jednorázové náhodně vygenerované řetězce, často s omezenou časovou platností. Hodí se všude tam, kde potřebujete něco jednorázově ověřit. Například ochránit odeslaný formulář proti CSRF, ověřit platnost zadané adresy zasláním kontrolního e-mailu apod.
Ochrana proti CSRF
Jednoduchou ukázkou je použití třídy k ochraně proti
Cross-site request forgery. Nejprve vytvoříme instanci třídy
Hot_Token
:
$token = new Hot_Token;
Instance si uvnitř sebe vygeneruje náhodný řetězec a uloží si jej do
sessions. Tento řetězec získáme pomocí metody getToken()
a
vložíme do formuláře, který chceme zabezpečit, typicky do
hidden pole:
<input type="hidden" name="token" value="<?php echo $token->getToken(); ?>" />
Při zpracování dat odeslaných z formuláře pak už jenom zavoláme
statickou metodu check()
, které jako parametr předáme přijatý
kontrolní řetězec:
Hot_Token::check($_POST['token']);
Ta jej zkontroluje oproti řetězci uloženému v sessions. Pokud je neplatný, vyhazuje metoda výjimku. V opačném případě se token zneplatní pro jakékoliv další budoucí použití a zpracování formuláře pokračuje normálně dál.
Časová platnost tokenu
Normálně není trvání tokenu nijak omezeno. Je ale více než rozumné
omezit jeho časovou platnost. K tomu slouží volání metody
setExpiration()
. Jako parametr se jí předává instance
standardní třídy Datetime
s požadovaným časem expirace:
$token = new Hot_Token;
$token->setExpiration(new Datetime('+1 hour'));
Nebo jednodušeji bez explicitního instancování Datetime
:
$token = new Hot_Token;
$token->setExpiration('+1 hour');
Nastavenou platnost můžete získat pomocí metody
getExpiration()
. Vrátí se instance třídy
Datetime
.
Ověření platnosti tokenu
Vedle statické metody check()
je druhým možným způsobem,
jak ověřit platnost získaného řetězce, jeho předání na parametr
konstruktoru. Pokud jej navíc chceme zneplatnit, slouží k tomu
metoda apply()
. Výše uvedený příklad se statickou metodou
check()
lze tedy se zcela stejným efektem přepsat jako:
$token = new Hot_Token($_POST['token']);
$token->apply();
Chcete-li jen zkontrolovat, zda je daná instance ještě platná, použijte
metodu isApplied()
.
Jak už bylo zmíněno, po ověření statickou metodou check()
se platnost tokenu automaticky ruší. Pokud chcete, aby byl token i po
ověření nadále platný, zadejte třetí parametr metody jako
FALSE
:
Hot_Token::check($_POST['token'], NULL, FALSE);
Navěšená data
Občas se může hodit k tokenu navěsit ještě nějaká další
data, která se předají společně s ním. K tomu slouží metody
getParam()
, setParam()
, issetParam()
a
unsetParam()
. Například při ochraně před CSRF můžeme chtít
kontrolovat nejen token, ale i konkrétní formulář, který se tokenem
kontroluje:
$token = new Hot_Token;
$token->setParam('formular', 'loremipsum');
Následuje klasické vložení do formuláře:
<input type="hidden" name="token" value="<?php echo $token->getToken(); ?>" />
A při zpracování máme navěšená data k dispozici:
$token = Hot_Token::check($_POST['token']);
if ($token->getParam('formular') != 'loremipsum') {
throw new Exception('Správný token u nesprávného formuláře.');
}
Vlastní storage
Data o tokenech se mohou ukládat i jinam, než do sessions, například do
databáze. Obecně si můžete vytvořit jakékoliv
uložiště. Stačí jen implementovat rozhraní
Hot_Token_Storage_Interface
a z toho vytvořenou instanci předat
na druhý parametr konstruktoru a metody check()
.
Pro příklad doporučuji podívat se v mé knihovně na třídu
Hot_Token_Storage_Session
, která je použita implicitně. Pokud
bychom na ní celý postup chtěli explicitně demonstrovat, vypadalo by to
nějak takto:
$storage = new Hot_Token_Storage_Session;
$token = new Hot_Token(NULL, $storage);
A následně při ověřování:
$storage = new Hot_Token_Storage_Session;
Hot_Token::check($_POST['token'], $storage);
Případně:
$storage = new Hot_Token_Storage_Session;
$token = new Hot_Token($_POST['token'], $storage);
Pokud budete nějakou vlastní storage implementovat, dovolím si jenom
upozornění, že pro předání dat z metody load()
je potřeba
použít přepravku, neboli instanci třídy Hot_Token_Crate
.
Další využití
Až se zase někdy rozhoupu sem něco napsat, zkusím jako ukázku konkrétní příklad pro ověření zadané e-mailové adresy. Pro dnešek je to zatím vše.
Jak se vám tokenová třída líbí?
Hezké. V ZendFrameworku na to máme special Form komponentu :P Je super, že dodržuješ Zendí konvenci pojmenování, takže To jde jednodušše použít :) Má to IMO totiž širší použití, než jen ochrana formů ;)
Tomáši, nemohl bys napsát pár dalších využití?
Díky
Díki moc, velmi good vec, dúfam, že to budem vedieť rozbehať :D
…a co pak teď vlastně děláš, když už dlouho neprogramuješ?
[4] Už jenom kibicuju ;)
A budou k té knihovně také Unit testy? Kazí nám to tu Code coverage :)