Я пытаюсь повысить безопасность форм на моем веб-сайте. Одна из форм использует 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>
Какие-либо предложения?
token_time
используется?token_time
. Я собирался ограничить время, в течение которого токен действителен, но еще не полностью реализовал код. Для ясности я удалил его из вопроса выше.Ответы:
Для кода безопасности не создавайте токены таким образом:
$token = md5(uniqid(rand(), TRUE));
rand()
предсказуемоuniqid()
только добавляет до 29 бит энтропииmd5()
не добавляет энтропии, а просто смешивает ее детерминированноПопробуйте это:
Создание токена 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 } }
Идем дальше с токенами Per-Form
Вы можете дополнительно ограничить доступность токенов только для определенной формы, используя
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
Любой, кто использует механизм создания шаблонов Twig, может воспользоваться упрощенной двойной стратегией, добавив этот фильтр в свою среду Twig:
$twigEnv->addFunction( new \Twig_SimpleFunction( 'form_token', function($lock_to = null) { if (empty($_SESSION['token'])) { $_SESSION['token'] = bin2hex(random_bytes(32)); } if (empty($_SESSION['token2'])) { $_SESSION['token2'] = random_bytes(32); } if (empty($lock_to)) { return $_SESSION['token']; } return hash_hmac('sha256', $lock_to, $_SESSION['token2']); } ) );
С помощью этой функции Twig вы можете использовать оба токена общего назначения следующим образом:
<input type="hidden" name="token" value="{{ form_token() }}" />
Или заблокированный вариант:
<input type="hidden" name="token" value="{{ form_token('/my_form.php') }}" />
Twig занимается только отображением шаблонов; вы по-прежнему должны правильно проверять токены. На мой взгляд, стратегия Twig предлагает большую гибкость и простоту, сохраняя при этом возможность максимальной безопасности.
Одноразовые токены CSRF
Если у вас есть требование безопасности, согласно которому каждый токен CSRF может быть использован ровно один раз, самая простая стратегия - восстановить его после каждой успешной проверки. Однако это приведет к аннулированию каждого предыдущего токена, что плохо сочетается с людьми, которые просматривают несколько вкладок одновременно.
Paragon Initiative Enterprises поддерживает библиотеку Anti-CSRF для этих критических случаев. Он работает исключительно с одноразовыми токенами формы. Когда в данных сеанса сохранено достаточное количество токенов (конфигурация по умолчанию: 65535), сначала будут циклически удаляться самые старые невыкупленные токены.
источник
Похоже, вам нужен еще один с вашим if.
if (!isset($_SESSION['token'])) { $token = md5(uniqid(rand(), TRUE)); $_SESSION['token'] = $token; $_SESSION['token_time'] = time(); } else { $token = $_SESSION['token']; }
источник
md5(uniqid(rand(), TRUE));
контекстам безопасности.Переменная
$token
не извлекается из сеанса, когда находится тамисточник