Poznajemy bardzo prosty patent na token CSRF omawiając cudzy kod. Do dzieła.
Klasa token jest bardzo prosta:
<?php
class Token {
public static function generate() {
return $_SESSION['token'] = base64_encode(openssl_random_pseudo_bytes(32));
}
public static function check($token) {
if(isset($_SESSION['token']) && $token === $_SESSION['token']) {
unset($_SESSION['token']);
return true;
}
return false;
}
}
Generowanie tokenu oraz porównywanie tego z sesji z tym przekazanym przez pole hidden formularza:
<form action="" method="POST">
<div class="product">
<strong>A product</strong>
<div class="field">
Quantity: <input type="text" name="quantity">
</div>
<input type="submit" value="Order">
<input type="hidden" name="product" value="1">
<input type="hidden" name="token" value="<?php echo Token::generate(); ?>">
</div>
</div>
</form>
W tak postawionej sprawie generate zapisuje token zarówno do sesji jak i do $_POST formularza (zaraz po submicie) i można to sobie porównać:
<?php
session_start();
require_once 'classes/Token.php';
if (isset($_POST['quantity'], $_POST['product'])) {
$product = $_POST['product'];
$quantity = $_POST['quantity'];
if (!empty($product) && !empty($quantity)) {
if(Token::check($_POST['token'])) {
echo 'Ok!!';
}
}
}
?>
Swoją drogą nie jest to najlepszy kod, ale proste pokazanie jak użyć tokenu. Zarówno session_start powinno dziać się automatycznie jak i klasy powinny mieć autoloading.
Guardy ifowe powinny być tak ustawione, aby dawały warunek oblania kończący skrypt, nie zaś warunek przejścia do kolejnego, zagnieżdżonego ifa.
Sami jednak te zasady często łamaliśmy, gdy trzeba było coś w prosty sposób pokazać.
W następnych lekcjach zobaczymy jak fachowo robić CSRF protection, także za pomocą middleware, czyli tak, jak się to robić powinno.