防御CSRF攻击的策略
目前防御 CSRF 攻击有以下四种策略(不全)
- 验证 HTTP Referer 字段
- 在请求地址中添加 token 并验证
- 在 HTTP 头中自定义属性并验证
- 验证码
(1)验证 HTTP Referer 字段
HTTP头中存在Refer 字段标记了请求的源地址,通过校验该字段可以检验是否为钓鱼网站的请求
优点:简单易行
缺陷:安全性不高,可能影响用户正常使用(Refer值由浏览器提供,实际上目前已经有一些方法可以篡改特定浏览器的Refer值,比如IE6和FF2;另外有些用户出于隐私考虑可能设置浏览器不提供Refer,可能导致误判为CSRF攻击)
(2)在请求地址中添加token并验证
登录时随机生成一个token,并存入session中,之后的请求需要将token在请求地址中带上,后端检验是否与session中的token相等
优点:比第一种方法具有更高的安全性
缺陷:对于动态生成的html代码无法使用js动态加入token,而需要在编码时手动加入;难以保证token本身的安全,比如论坛等,黑客可以发布自己的网址,服务器也会给它加上token,以此获得token的值(系统可以判断是否为本站地址来选择是否给请求带上token),不过,即使这个 csrftoken 不以参数的形式附加在请求之中,黑客的网站也同样可以通过 Referer 来得到这个 token 值以发动 CSRF 攻击,这也是一些用户喜欢手动关闭浏览器 Referer 功能的原因。
举例:
令牌生成函数(gen_token())
function gen_token() {
//这里我是贪方便,实际上单使用Rand()得出的随机数作为令牌,也是不安全的。
//这个可以参考我写的Findbugs笔记中的《Random object created and used only once》
$token = md5 ( uniqid ( rand () , true ));
return $token ;
}
Session令牌生成函数(gen_stoken())
<? php
function gen_stoken() {
$pToken = "" ;
if( $_SESSION [STOKEN_NAME] == $pToken ){
//没有值,赋新值
$_SESSION [STOKEN_NAME] = gen_token() ;
}
else{
//继续使用旧的值
}
}
?>
WEB表单生成隐藏输入域的函数
<?php
function gen_input() {
gen_stoken();
echo “<input type=\”hidden\” name=\”" . FTOKEN_NAME . “\”
value=\”" . $_SESSION[STOKEN_NAME] . “\”> “;
}
?>
WEB表单结构
<? php
session_start ();
include (”functions . php”);
?>
< form method = ”POST” action = ”transfer . php” >
< input type = ”text” name = ”toBankId” >
< input type = ”text” name = ”money” >
<? gen_input(); ?>
< input type = ”submit” name = ”submit” value = ”Submit” >
</ FORM >
最后服务端核对令牌
(3)服在 HTTP 头中自定义属性并验证
和上一种方法类似,不同的是将token作为参数放在HTTP头中的自定义属性中,可以通过XMLHttpRequest类给所有请求加上csrftoken这个Http头属性,并存入token数值
缺陷:这种方法的局限性非常大。XMLHttpRequest 请求通常用于 Ajax 方法中对于页面局部的异步刷新,并非所有的请求都适合用这个类来发起,而且通过该类请求得到的页面不能被浏览器所记录下,从而进行前进,后退,刷新,收藏等操作,给用户带来不便。另外,对于没有进行 CSRF 防护的遗留系统来说,要采用这种方法来进行防护,要把所有请求都改为 XMLHttpRequest 请求,这样几乎是要重写整个网站,这代价无疑是不能接受的。
(4)验证码略