为了防御 CSRF 攻击,Web 应用程序可以将秘密令牌嵌入到页面中。所有来自这些页面的请求都必须携带此令牌,否则会被视为跨站请求,不会具有和同源请求相同的权限。攻击者无法获取此秘密令牌,因此他们的请求很容易被识别为跨站请求。
Elgg} 使用这种秘密令牌方法作为其内置的防护措施来防御 CSRF 攻击。我们已经关闭了这些防护措施以使攻击生效。 Elgg 在请求中嵌入了两个参数 __elgg_ts 和 __elgg_token。服务器在处理请求前会验证它们。
将秘密令牌和时间戳嵌入到网页中:Elgg 在所有需要用户操作的地方都添加了安全令牌和时间戳。以下 HTML 代码出现在所有的表里。这两个隐藏字段会在表单提交时被附加到请求中:
<input type = "hidden" name = "__elgg_ts" value = "" />
<input type = "hidden" name = "__elgg_token" value = "" />
Elgg 还将安全令牌和时间戳的值赋给 JavaScript 变量,以便页面上的 JavaScript 代码可以轻松访问这些变量。
elgg.security.token.__elgg_ts;
elgg.security.token.__elgg_token;
在 Elgg} 的网页中添加安全令牌和时间戳是通过 vendor/elgg/elgg/views/default/input/securitytoken.php 来实现的。下面的代码片段展示了这些内容是如何动态地添加到页面上的。
$ts = time();
$token = elgg()->csrf->generateActionToken($ts);
echo elgg_view('input/hidden', ['name' => '__elgg_token', 'value' => $token]);
echo elgg_view('input/hidden', ['name' => '__elgg_ts', 'value' => $ts]);
秘密令牌的生成。Elgg 的安全令牌是下面信息产生的哈希值:网站提供的秘密值,时间戳、用户会话 ID 和随机生成的会话字符串。下面的代码展示了 Elgg 中的安全令牌的生成过程(在 vendor/elgg/elgg/engine/classes/Elgg/Security/Csrf.php 中)。
/**
* 从会话令牌、时间戳和站点密钥生成一个令牌。
*/
public function generateActionToken($timestamp, $session_token = '') {
if (!$session_token) {
$session_token = $this->session->get('__elgg_session');
if (!$session_token) {
return false;
}
}
return $this->hmac
->getHmac([(int) $timestamp, $session_token], 'md5')
->getToken();
}
秘密令牌验证。 Elgg} 网站会验证生成的令牌和时间戳以防御 CSRF 攻击。每当用户执行某个操作时,都会调用 Csrf.php 中的 validate() 函数,此函数会对这些令牌进行验证。如果令牌不存在或无效,则将拒绝该操作并将用户重定向到其他页面。我们在该函数开始添加了一个 return 语句,从而禁用了这一功能。
public function validate(Request $request) {
(*@\textbf{return;}@*) // 为 SEED 实验(禁用 CSRF 防护措施)而添加
$token = $request->getParam('__elgg_token');
$ts = $request->getParam('__elgg_ts');
... (省略了代码)
}
任务:开启防护措施。为了启用防护措施,请进入 Elgg 容器,前往 /var/www/elgg/vendor/elgg/elgg/engine/classes/Elgg/Security 文件夹,从 Csrf.php 中删除 return 语句。容器内有一个简单的编辑器叫做 nano。在完成更改后,请重新运行攻击,并观察您的攻击是否成功。请指出捕获的 HTTP 请求中的秘密令牌。请解释为什么攻击者无法在 CSRF 攻击中发送这些秘密令牌;是什么阻止了他们从网页中找到这些秘密令牌?
需要注意的是(非常重要),当我们启用防护措施后进行CSRF攻击去修改个人资料时,攻击失败后攻击者的页面会被重新载入,这将再次触发伪造的 POST 请求。这会导致另一个失败的攻击,然后页面将继续被重新加载并发送出另一个伪造的 POST 请求。这种无限循环可能会导致您的计算机变慢。因此,在确认攻击失败后,请关闭页面以停止无限循环。