如何使用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

回到顶部