对于安全代码,请不要以这种方式生成令牌:$token = md5(uniqid(rand(), TRUE));
- rand()是可以预见的 https://jazzy.id.au/2010/09/20/cracking_random_number_generators_part_1.html
- uniqid()仅添加最多 29 位熵 http://securitymaverick.com/php-uniqid-entropy-analysis-and-potentially-vulnerable-apps
-
md5()
不增加熵,它只是确定性地混合它
试试这个:
生成 CSRF 令牌
PHP 7
session_start();
if (empty($_SESSION['token'])) {
$_SESSION['token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['token'];
旁注:其中之一我雇主的开源项目 https://paragonie.com/projects是一项向后移植的举措random_bytes()
and random_int()
到 PHP 5 项目中。它已获得 MIT 许可,并可在 Github 和 Composer 上作为paragonie/random_compat https://github.com/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 令牌
不要只使用==
甚至===
, use hash_equals() https://secure.php.net/hash_equals(仅限 PHP 5.6+,但可用于早期版本哈希兼容 https://github.com/indigophp/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() https://secure.php.net/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 模板引擎 http://twig.sensiolabs.org通过将此过滤器添加到其 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 保持抗CSRF库 https://github.com/paragonie/anti-csrf对于这些极端情况。它专门与一次性执行表单令牌一起使用。当会话数据中存储了足够的令牌(默认配置:65535)时,它将首先循环出最旧的未兑换令牌。