PHP - CSRF - Как сделать его, работает на всех вкладках?

Я читал о том, как предотвратить CSRF-нападения в прошлые дни. Я собираюсь обновить маркер в каждом pageload, сохранить маркер на сессии и осуществить проверку при представлении формы.

Но что, если пользователь имеет, позволяет, говорят, что 3 вкладки, открытые с моим веб-сайтом, и я просто храню последний маркер на сессии? Это перезапишет маркер с другим маркером, и некоторое постдействие собирается перестать работать.

Я должен сохранить все маркеры на сессии, или есть ли лучшее решение получить эту работу?

16
задан Marcel Korpel 28 April 2013 в 10:43
поделиться

1 ответ

Да, при подходе с сохраненными токенами вам придется хранить все сгенерированные токены на случай, если они вернутся в любой момент. Один сохраненный токен не работает не только для нескольких вкладок / окон браузера, но и для навигации назад / вперед. Обычно вы хотите управлять потенциальным взрывом хранилища за счет истечения срока действия старых токенов (по возрасту и / или количеству токенов, выпущенных с тех пор).

Другой подход, который полностью исключает хранение токенов, - это выпуск подписанного токена, сгенерированного с использованием секрета на стороне сервера. Затем, когда вы получите токен обратно, вы можете проверить подпись, и если она совпадает, вы знаете, что подписали ее. Например:

// 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-атака не привели к утечке токена, который будет работать для этого пользователя вечно, и значение, которое изменится пароль сбрасывается, так что изменение пароля делает существующие токены недействительными.

30
ответ дан 30 November 2019 в 17:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: