如何使用PHP正确添加跨站点请求伪造(CSRF)令牌
我正在尝试为我的网站上的表单添加一些安全性。一种形式是使用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 = md5(uniqid(rand(), TRUE));
rand()
是可以预见的uniqid()
最多只加29位熵md5()
不添加熵,而是确定性地将其混合
试试看:
回答:
PHP 7
session_start();if (empty($_SESSION['token'])) {
$_SESSION['token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['token'];
旁注:我老板的一个开源项目是一项向后移植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'];
回答:
不要只是使用==
甚至===
使用hash_equals()
(仅适用于PHP5.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']
。
奖励:混合方法+ 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令牌只能使用一次,则最简单的策略是在每次成功验证后重新生成它。但是,这样做会使之前的所有令牌失效,而这与一次浏览多个选项卡的人不能很好地融合在一起。
Paragon Initiative Enterprises为这些极端情况维护了一个反CSRF库。它仅与一次性使用的形式令牌一起使用。当会话数据中存储了足够的令牌(默认配置:65535)时,它将首先循环出最旧的未兑换令牌。
以上是 如何使用PHP正确添加跨站点请求伪造(CSRF)令牌 的全部内容, 来源链接: utcz.com/qa/421564.html