Я прочитал о том, как предотвратить 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 не просачивают токен, который будет работать для этого пользователя навсегда, а значение, которое изменяется при сбрасывании пароля, так что смена пароля делает недействительными существующие токены.
Вы можете просто использовать токен, который является постоянным для текущего сеанса или даже для пользователя (например, хэш хэша пароля пользователя) и не может быть определен сторонним пользователем (например, использование хэша IP-адреса пользователя является плохим) ,
Тогда вам не нужно хранить, возможно, тонны сгенерированных токенов, и если сеанс не истечет (что, вероятно, потребует от пользователя входа в систему в любом случае), пользователь может использовать столько вкладок, сколько захочет.