攻击者做一个页面,里面藏一个自动提交的表单,目标是你网站的转账或改密接口。用户恰好登录着你的站,浏览器带着cookie把请求发出去了,服务端一看登录态有效,照办。整个过程攻击者碰不到cookie一个字节——这就是CSRF。
防御核心是让攻击者猜不到的东西出现在请求里:一次性token。表单里埋一个随机值,服务端存session里一份,提交时两边对,对不上就拒。
<?php
// 出表单时生成token
if (empty($_SESSION['csrf'])) {
$_SESSION['csrf'] = bin2hex(random_bytes(16));
}
// 模板里埋进表单
// <input type="hidden" name="csrf" value="<?php echo $_SESSION['csrf']; ?>">
// 提交时校验
function csrf_check() {
$t = $_POST['csrf'] ?? '';
if (!hash_equals($_SESSION['csrf'], $t)) {
http_response_code(403);
exit('非法请求');
}
// 一次性使用,用完即换
$_SESSION['csrf'] = bin2hex(random_bytes(16));
}
csrf_check();
// ... 正常业务逻辑
hash_equals换成等号比较也有隐患:值不等时 Ordinary comparison leaks timing information,虽然实际利用难度高,但函数都在那了没理由不用。token用完即换,防重放。
三层防线一起上更稳:cookie加SameSite=Lax(前面session篇讲过),POST表单强制token,敏感操作二次确认改密码要输旧密码、转账要输验证码。CSRF的可怕在于全程发生在用户浏览器里,日志只能看到正常登录态的合法请求——所以必须在请求本身里放攻击者无法伪造的东西。
A5创业网 版权所有