PHP – CSRF(跨站点请求伪造漏洞)


首字母缩略词“CSRF”代表跨站点请求伪造。CSRF 是一种 Internet 漏洞,涉及受信任的网站用户发出未经授权的命令。通过采取本章中介绍的措施,可以向 PHP Web 应用程序提供足够的保护以抵御此攻击。

默认情况下,浏览器使用 “GET” 请求方法来发送数据。这通常用作 CSRF 的利用点。为了将命令注入特定网站,攻击者使用“IMG”等 HTML 标签。例如,Web 应用程序的 url 端点(如“/delete.php?empcode=1234”)会删除从 GET 请求的 empcode 参数传递的帐户。现在,如果经过身份验证的用户在任何其他应用程序中遇到以下脚本。


<img src="http://example.com/delete.php?empcode=1234" 
   width="0" height="0" border="0">

无意中导致与 empcode=1234 相关的数据被删除。

此问题的常见解决方法是使用 CSRF 令牌。CSRF 令牌是嵌入到请求中的一串随机字符,以便 Web 应用程序可以相信已按照正常工作流程从预期来源收到请求。

实施 CSRF 的步骤

在 PHP 中实现 CSRF 令牌保护的步骤如下 -

  • 通过启动新 session 来启动脚本。
  • 生成随机字符的令牌。您可以使用 PHP 提供的几个内置函数中的任何一个来生成随机字符串。让我们使用 md5() 函数来获取生成唯一随机字符串的 uniqueid() 函数的哈希值。
  • 在要提供给用户提交数据的 HTML 表单中,包含一个隐藏文件,其值作为上述步骤中生成的随机标记。
  • 然后,服务器会在表单提交后根据用户会话验证令牌,以消除恶意请求。
  • 您还可以添加另一个 session 变量,其值为当前时间,并发送过期时间以进行验证。

例子

以下是实现 CSRF 令牌验证机制的 PHP 代码。以下脚本生成令牌并嵌入到 HTML 表单中。


<?php
   session_start();
   if(!isset($_SESSION["csrf_token"])) {
   
      // 没有令牌,生成一个新的令牌
      $token = md5(uniqid(rand(), true));
      $_SESSION["csrf_token"] = $token;
      
   } else {
   
      // 重复使用令牌
      $token = $_SESSION["csrf_token"];        
   }
?>
<html>
<body>
   <form method="get" action="test.php">
      <input type="text" name="empcode" placeholder="empcode" />
      <input type="hidden" name="csrf_token" value="<?php echo $token;?>" />
      <input type="submit" />
   </form>
</body>
</html>

表单将提交到“test.php”脚本,如下所示 -


<?php
   session_start();
   echo "hello";
   if ($_GET["csrf_token"] == $_SESSION["csrf_token"]) {

      // 重置令牌
      echo $_GET["csrf_token"] . "<br>";
      echo $_SESSION["csrf_token"] . "<br>";
      echo "<h3>CSRF令牌验证成功。继续采取进一步行动</h3>";
   } else {
      echo "<h3>CSRF令牌验证失败</h3>";
   }
?>

它将产生以下输出 -

PHP Csrf

要模拟 CSRF 验证失败,请打开浏览器的 检查 工具,手动编辑 hidden 字段中的值并提交表单以查看令牌不匹配,从而导致验证失败。