如何使用 PHP 正确添加跨站请求伪造 (CSRF) 令牌

2024-06-19

我正在尝试为我网站上的表单添加一些安全性。其中一个表单使用 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 :

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

有什么建议么?


对于安全代码,请不要以这种方式生成令牌:$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)时,它将首先循环出最旧的未兑换令牌。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 PHP 正确添加跨站请求伪造 (CSRF) 令牌 的相关文章

  • 删除重复字符

    我如何删除重复字符 例如删除字母k in cakkkke让它成为cake 执行此操作的一种简单方法是循环遍历字符串的每个字符 如果该字符不是前一个字符的重复 则将字符串的每个字符附加到新字符串 下面是一些可以执行此操作的代码 newStri
  • 升级 PHP 本地实现的 Olson tz 数据库

    我正在开发一个大量使用时区的网站 该网站需要处于可用的绝对最新 tz 信息的最前沿 我已经通过tz 邮件列表 http news gmane org gmane comp time tz 我不知道的是如何让 PHP 安装在我的开发计算机和
  • 阻止跨域调用 asp.net .asmx Web 服务

    我构建了一个应用程序 它使用 jQuery 和 JSON 来使用 ASP NET asmx Web 服务来执行增删改查操作 应用程序和 asmx 位于同一域中 我不介意人们远程消耗 asmx 的读取操作 但不希望人们随机删除内容 我可以将我
  • 有没有办法在 PHP 中进行 100% 面向对象的编程?

    我想 可以在单独的类中调用函数 我在 PHP 中称之为面向对象编程 但一开始总有一个index php什么的 它调用或者实例化另一个类 有没有办法让一个类像java那样进行自调用 public static void main String
  • PHPUnit 测试套件包含路径

    使用 phpunit 时 我在包含路径方面遇到了一些麻烦 不是针对 phpunit 本身 而是针对我的代码和测试目录 我有以下代码结构 Application StringCalculator php tests StringCalcula
  • 如何获取发送的 PHP 卷曲请求的信息

    我正在尝试调试对 Web 服务 getToken 端点的curl 请求 我不能 100 确信 URL 和身份验证信息已正确写入curl 句柄 我正在尝试使用curl getinfo ch CURLINFO HEADER OUT 捕获发送的请
  • CodeIgniter“找不到您请求的页面。”错误?

    我在使用 CodeIgniter 时遇到问题 我已经检查了互联网上所有可能的解决方案 似乎对我的情况没有任何帮助 我不是一个大专业人士 这是我第一次使用 CodeIgniter 所以不要对我严厉 路线 php route default c
  • Zend Framework 调用另一个控制器操作

    您好 我在这里遇到调用另一个控制器操作来发送邮件的问题 这是我的代码 user php public function followAction follow id this gt getParam id response a href c
  • WooCommerce 订阅 - 获取特定订阅的产品

    有没有办法从 product from subscription 谢谢这个帖子 https stackoverflow com questions 42791096 woocommerce subscriptions get related
  • PHP 和 ESB(使用 Mule)(ESB:企业服务总线)

    您在 PHP 项目中何时 何地以及为何使用 ESB 您认为在何处 何时以及为何在 PHP 项目中使用 ESB 有意义 ESB 以及像 Mule 这样的 ESB 促进者 是否提供了 PHP 和本地 LAMP 技术所缺乏的任何功能 Edit 我
  • 如何使用 .Net (C#) 在 Windows 中安全地存储 AES 密钥?

    我一直在寻找一种方法来存储给定的 AES 密钥 以便无法检索它 但它仍然可以用于加密和解密 使用 C 我认为可以找到非对称密钥存储的等效项here http msdn microsoft com en us library tswxhw92
  • 如何显示浏览器中发出的 API 请求的 symfony 分析器?

    我正在使用 Symfony2 FOSRest 捆绑包开发 REST api 我想知道是否有任何方法可以在开发模式下调用api app dev php 从浏览器 对应于Accept text html application xhtml xm
  • htaccess 文件中的动态重写

    我正在我的 htaccess 文件中进行一些重写和重定向 我这样做的原因是因为我正在为现有网站开发新的设计和布局 因此我需要重定向以保持谷歌排名等 所以旧的 现有的 URL 看起来像这样 news internet shopper numb
  • 将 Zend Framework 最小化为 Zend_Mail? [复制]

    这个问题在这里已经有答案了 可能的重复 在没有实际框架的情况下使用 Zend Framework 组件 https stackoverflow com questions 1402989 use zend framework compone
  • 将父产品名称添加到 WooCommerce 中的每个购物车项目名称中

    我想在购物车页面中显示父产品名称和子产品名称 购物车项目 以供我使用分组产品 我在链接产品 gt 添加子产品的分组产品下选择父产品数据作为分组产品 模板中的代码cart php echo apply filters woocommerce
  • PHP exec() 返回值是什么?

    我正在尝试使用 PHP exec 函数 如果 return var 参数与输出参数一起存在 那么执行命令的返回状态将被写入此 多变的 如果执行成功 则为 0 但是 如果出现错误 则可能是多个其他整数 我似乎无法在任何地方找到这些整数对应的内
  • ulimit -r 返回不同的值

    我将以下两行添加到系统范围的 etc security limits conf 中 soft rtprio 55 hard rtprio 55 系统重新启动后 根据我在计算机上访问用户帐户的方式 我会得到两个不同的结果 user clien
  • Zend Framework 中数据库驱动路由的教程?

    我正在开发一个需要使用数据库驱动的 MVC 方案的项目 其中控制器和视图的路由通过单个数据库表进行控制 但是 我无法找到任何使用当前版本的框架演示这一点的教程 它们似乎都是在几个版本之前编写的 我想知道是否有人用更新的版本做过类似的事情框架
  • PDO 和 Microsoft SQL:必须声明表变量“@P1”

    我正在尝试使用 PDO 中的绑定从 Microsoft SQL 数据库中选择一些条目 我正在使用的代码看起来与我在文档中找到的代码类似 但是 当我运行它时 我收到以下警告 警告 PDOStatement execute pdostateme
  • 如何确定 n 高数字金字塔中的最大路线成本

    我有一个像这样的数字金字塔 7 4 8 1 8 9 2 4 6 7 4 6 7 4 9 4 9 7 3 8 8 routes 32 每个数字都按其系列中的强大程度进行索引 0 9 gt 1 1 8 gt 5 2 8 gt 4 3 7 gt

随机推荐