Подтвердить что ты не робот

Как правильно добавить токен CSRF с помощью PHP

Я пытаюсь добавить некоторую безопасность в формы на моем веб-сайте. Одна из форм использует AJAX, а другая - простая форма "связаться с нами". Я пытаюсь добавить токен CSRF. Проблема, с которой я сталкиваюсь, заключается в том, что токен только отображается в HTML-значении некоторое время. В остальное время значение пусто. Вот код, который я использую в форме AJAX:

PHP:

if (!isset($_SESSION)) {
    session_start();
$_SESSION['formStarted'] = true;
}
if (!isset($_SESSION['token']))
{$token = md5(uniqid(rand(), TRUE));
$_SESSION['token'] = $token;

}

HTML

 <form>
//...
<input type="hidden" name="token" value="<?php echo $token; ?>" />
//...
</form>

Любые предложения?

4b9b3361

Ответ 1

Для кода безопасности не генерируйте эти жетоны следующим образом: $token = md5(uniqid(rand(), TRUE));

Попробуйте следующее:

Создание токена CSRF

PHP 7

session_start();
if (empty($_SESSION['token'])) {
    $_SESSION['token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['token'];

Sidenote: один из моих проектов с открытым исходным кодом моего работодателя - это инициатива по обращению random_bytes() и random_int() в проекты PHP 5. Он MIT лицензирован и доступен в Github и Composer как paragonie/random_compat.

PHP 5.3+ (или с ext-mcrypt)

session_start();
if (empty($_SESSION['token'])) {
    if (function_exists('mcrypt_create_iv')) {
        $_SESSION['token'] = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
    } else {
        $_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(32));
    }
}
$token = $_SESSION['token'];

Проверка токена CSRF

Не используйте только == или даже ===, используйте hash_equals() (только PHP 5.6+, но доступный ранее версии с библиотекой hash-compat).

if (!empty($_POST['token'])) {
    if (hash_equals($_SESSION['token'], $_POST['token'])) {
         // Proceed to process the form data
    } else {
         // Log this as a warning and keep an eye on these attempts
    }
}

Далее с использованием токенов в одной форме

Вы также можете ограничить токены только доступными для конкретной формы, используя hash_hmac(). HMAC - это особая ключевая хеш-функция, которая безопасна в использовании даже при более слабых хеш-функциях (например, MD5). Тем не менее, я рекомендую использовать семейство хеш-функций SHA-2.

Во-первых, сгенерируйте второй токен для использования в качестве ключа HMAC, а затем используйте для этого такую ​​логику:

<input type="hidden" name="token" value="<?php
    echo hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
?>" />

И затем используя конгруэнтную операцию при проверке токена:

$calc = hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
if (hash_equals($calc, $_POST['token'])) {
    // Continue...
}

Токены, сгенерированные для одной формы, не могут быть повторно использованы в другом контексте, не зная $_SESSION['second_token']. Важно, чтобы вы использовали отдельный токен в качестве ключа HMAC, чем тот, который вы просто запустили на странице.

Бонус: гибридный подход + интеграция Twig

Любой, кто использует

Или заблокированный вариант:

<input type="hidden" name="token" value="{{ form_token('/my_form.php') }}" />

Twig касается только рендеринга шаблона; вы все равно должны правильно проверить токены. По моему мнению, стратегия Twig обеспечивает большую гибкость и простоту, сохраняя при этом максимальную безопасность.


Одноразовые токены CSRF

Если у вас есть требование безопасности, чтобы каждый токен CSRF был доступен для использования ровно один раз, простейшая стратегия восстанавливает его после каждой успешной проверки. Однако это приведет к недействительности каждого предыдущего токена, который не будет хорошо сочетаться с людьми, которые просматривают сразу несколько вкладок.

Paragon Initiative Enterprises поддерживает анти-CSRF-библиотеку для этих угловых случаев. Он работает только с одноразовыми токенами для каждой формы. Когда в данных сеанса хранятся достаточное количество токенов (конфигурация по умолчанию: 65535), сначала будет выкачать самые старые неиспользуемые маркеры.

Ответ 2

Предупреждение о безопасности: md5(uniqid(rand(), TRUE)) не является безопасным способом генерации случайных чисел. См. этот ответ для получения дополнительной информации и решения, которое использует криптографически безопасный генератор случайных чисел.

Похоже, вам нужно другое с вашим if.

if (!isset($_SESSION['token'])) {
    $token = md5(uniqid(rand(), TRUE));
    $_SESSION['token'] = $token;
    $_SESSION['token_time'] = time();
}
else
{
    $token = $_SESSION['token'];
}

Ответ 3

Переменная $token не извлекается из сеанса, когда она там