Я читал о том, как предотвратить CSRF-нападения в прошлые дни. Я собираюсь обновить маркер в каждом pageload, сохранить маркер на сессии и осуществить проверку при представлении формы.
Но что, если пользователь имеет, позволяет, говорят, что 3 вкладки, открытые с моим веб-сайтом, и я просто храню последний маркер на сессии? Это перезапишет маркер с другим маркером, и некоторое постдействие собирается перестать работать.
Я должен сохранить все маркеры на сессии, или есть ли лучшее решение получить эту работу?
Да, при подходе с сохраненными токенами вам придется хранить все сгенерированные токены на случай, если они вернутся в любой момент. Один сохраненный токен не работает не только для нескольких вкладок / окон браузера, но и для навигации назад / вперед. Обычно вы хотите управлять потенциальным взрывом хранилища за счет истечения срока действия старых токенов (по возрасту и / или количеству токенов, выпущенных с тех пор).
Другой подход, который полностью исключает хранение токенов, - это выпуск подписанного токена, сгенерированного с использованием секрета на стороне сервера. Затем, когда вы получите токен обратно, вы можете проверить подпись, и если она совпадает, вы знаете, что подписали ее. Например:
// Only the server knows this string. Make it up randomly and keep it in deployment-specific
// settings, in an include file safely outside the webroot
//
$secret= 'qw9pDr$wEyq%^ynrUi2cNi3';
...
// Issue a signed token
//
$token= dechex(mt_rand());
$hash= hash_hmac('sha1', $token, $secret);
$signed= $token.'-'.$hash;
<input type="hidden" name="formkey" value="<?php echo htmlspecialchars($signed); ?>">
...
// Check a token was signed by us, on the way back in
//
$isok= FALSE;
$parts= explode('-', $_POST['formkey']);
if (count($parts)===2) {
list($token, $hash)= $parts;
if ($hash===hash_hmac('sha1', $token, $secret))
$isok= TRUE;
}
Таким образом, если вы получаете токен с соответствующей подписью, вы знаете, что вы его сгенерировали. Само по себе это не очень помогает, но тогда вы можете добавить в токен дополнительные вещи, кроме случайности, например идентификатор пользователя:
$token= dechex($user->id).'.'.dechex(mt_rand())
...
if ($hash===hash_hmac('sha1', $token, $secret)) {
$userid= hexdec(explode('.', $token)[0]);
if ($userid===$user->id)
$isok= TRUE
Теперь каждая отправка формы должна быть авторизована тем же пользователем, который взял форму, что довольно много поражений CSRF.
Еще одна вещь, которую стоит добавить в токен, - это время истечения срока действия, чтобы мгновенная компрометация клиента или MitM-атака не привели к утечке токена, который будет работать для этого пользователя вечно, и значение, которое изменится пароль сбрасывается, так что изменение пароля делает существующие токены недействительными.