Přeskočit na hlavní obsah

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í?

Komentáře

  1. 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ů ;)

  2. Tomáši, nemohl bys napsát pár dalších využití?
    Díky

  3. Díki moc, velmi good vec, dúfam, že to budem vedieť rozbehať :D

  4. …a co pak teď vlastně děláš, když už dlouho neprogramuješ?

  5. [4] Už jenom kibicuju ;)

  6. A budou k té knihovně také Unit testy? Kazí nám to tu Code coverage :)