跨站请求伪造
问题
跨站点请求伪造或 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 令牌窃取和修复。