跨站请求伪造

问题

跨站点请求伪造或 CSRF 可以强制最终用户在不知不觉中生成对 Web 服务器的恶意请求。可以在 POST 和 GET 请求中利用此攻击媒介。例如,url 端点/delete.php?accnt=12 删除从 GET 请求的 accnt 参数传递的帐户。现在,如果经过身份验证的用户将在任何其他应用程序中遇到以下脚本

<img src="http://domain.com/delete.php?accnt=12" width="0" height="0" border="0">

该帐户将被删除。

该问题的常见解决方案是使用 CSRF 令牌。CSRF 令牌嵌入到请求中,以便 Web 应用程序可以信任请求来自预期源,作为应用程序正常工作流的一部分。首先,用户执行一些操作,例如查看表单,触发创建唯一令牌。实现此功能的示例表单可能如下所示

<form method="get" action="/delete.php">
  <input type="text" name="accnt" placeholder="accnt number" />
  <input type="hidden" name="csrf_token" value="<randomToken>" />
  <input type="submit" />
</form>

然后,在表单提交之后,服务器可以针对用户会话验证令牌以消除恶意请求。

示例代码

以下是基本实现的示例代码:

/* Code to generate a CSRF token and store the same */
...
<?php
  session_start();
  function generate_token() {
    // Check if a token is present for the current session
    if(!isset($_SESSION["csrf_token"])) {
        // No token present, generate a new one
        $token = random_bytes(64);
        $_SESSION["csrf_token"] = $token;
    } else {
        // Reuse the token
        $token = $_SESSION["csrf_token"];
    }
    return $token;
  }
?>
<body>
  <form method="get" action="/delete.php">
    <input type="text" name="accnt" placeholder="accnt number" />
    <input type="hidden" name="csrf_token" value="<?php echo generate_token();?>" />
    <input type="submit" />
  </form>
</body>
...

/* Code to validate token and drop malicious requests */
...
<?php
  session_start();
  if ($_GET["csrf_token"] != $_SESSION["csrf_token"]) {
    // Reset token
    unset($_SESSION["csrf_token"]);
    die("CSRF token validation failed");
  }
?>
...

现有许多库和框架都有自己的 CSRF 验证实现。虽然这是 CSRF 的简单实现,但你需要编写一些代码来动态重新生成 CSRF 令牌,以防止 CSRF 令牌窃取和修复。