关键处理
Web应用中,用户登陆后执行操作中,有一些一旦完成就无法撤销,如信用卡支付,修改密码,发送邮件等,这些被称为“关键处理”。
关键处理中如果存在安全隐患,就会产生名为Cross-Site Request Forgeries简称CSRF的漏洞。
可能会遭受以下攻击:
1.使用用户的账号购物
2.删除用户账号
3.使用用户的账号发布帖子
4.更改用户的密码或邮箱地址
1.输入-执行 模式下的攻击手段
登陆 -- 密码输入 -- 执行更改密码
需满足3个条件:
1.用POST方法请求执行更改密码
2.保持登录状态
3.用POST参数中的pwd制定新密码
脚本举例:
<body οnlοad="document.form[0].submit()">
<form action="http://xxxx.php" method="POST">
<input type="hidden" name="pwd" value="cracked"
</form>
</body>
一般攻击流程:
0.用户登录
1.攻击者设下圈套
2.受害人浏览恶意网站而触发圈套
3.攻击者使用恶意网站中的JavaScipt,使受害人的浏览器向攻击对象网站发送将密码变更为cracked的POST请求
4.密码被更改
CSRF和XSS区别
相同点
1.浏览恶意网站 ->
2.恶意HTML <-
3.攻击用请求 ->
CSRF是指恶意使用服务器对步骤3中请求的处理,恶意使用的内容仅限于服务器提供的处理。
XSS情况下3中的请求包含的脚本则被原封不动的以响应的形式返回,随后该恶意脚本在用户的浏览器中执行。由于攻击者能够在用户的浏览器上执行自己准备的HTML或JavaScript,因此只要是浏览器能做到的事都可以被用作攻击手段。攻击者甚至还能够通过JavaScript恶意使用服务器端的功能。
2.存在确认页面的CSRF攻击
输入页面和执行页面之间包含确认页面的情况。
下面以更改邮箱地址的操作为例。
确认页面将数据传递给你执行页面的方法大体上有两种。一种使用hidden参数(type属性为hidden的input元素),另一种使用会话变量。
使用hidden参数传递参数
输入页面中输入的邮箱地址被以hidden参数的形式嵌入在确认页面中,然后又被传递给了执行页面。
输入->确认
POST
<input type="text" name="mail" value="xxx@xxx">
确认->执行
hidden
<input type="hidden" name="mail" value="xxx@xxx”>
此模式下的CSRF攻击手段与没有确认页面时的情况相同,这是因为执行页面性输入(HTTP请求)中取得邮箱地址信息这一点与之前例子一样。所以上面介绍的恶意HTML几乎是被直接用来攻击的。
使用会话变量传递参数
输入->确认
POST
<input name="mail" value="xxx@xxx">
确认页面 将邮箱地址保存至会话变量
$_SESSION['mail']=$_POST['mail']
执行页面 从会话变量中获取邮箱地址
$mail=$_SESSION['mail']
攻击阶段:
1.向确认页面发送POST请求,使邮箱地址保存到会话变量中
2.伺机打开执行页面
实现上述两个阶段共计需要用到两个iframe元素。
iframe1与恶意网页同时打开,并向确认页面发送含有邮箱地址的POST请求。这样一来邮箱地址就被保存到了会话变量中。
在恶意网页打开10秒钟后,iframe2打开执行页面并完成CSRF攻击。这时,由于邮箱地址已被设置到会话变量中,因此邮箱地址就被更改为了攻击者所指定的邮箱地址,攻击成功。
安全隐患的产生原因
CSRF漏洞之所以能产生,是因为Web应用存在以下特性:
1.form元素的action属性能够指定任一域名的URL
2.保存在Cookie中的会话ID会被自动发送给对象网站
1的问题在于,即使是恶意网站,也能够向攻击目标网站发送请求。而2的问题则在于,即使请求经过了恶意网站,会话ID的Cookie值也照样发送,从而导致攻击请求在认证的状态下被发送。
在HTTP请求中 Referer字段,CSRF指向的是恶意网站,而正常的不是,只有这点差别。
但是通常情况下,Web应用中并不会检验Referer值,所以如果开发者没有意识去确认该请求是否由正规用户自愿发送,就无法区分两者,会引入CSRF漏洞。
对策
防御CSRF的关键为为确认关键处理的请求确实是由正规用户自愿发送的。因此,作为CSRF的防范策略,需执行以下两点:
1.筛选出需要防范CSRF攻击的页面
2.使代码有能力辨认是否是正规用户的自愿请求
筛选出需要防范CSRF攻击的页面
例如电商网站,需要防范的是‘购买’和‘更改'界面。
在开发过程中,应当执行以下流程:
1.在需求分析阶段制作功能一览表,标记出需要执行CSRF防范策略的功能
2.在概要设计阶段制作页面跳转图,标记出需要执行CSRF防范策略的页面
2.在开发阶段实施CSRF防范策略
具体开发方法
确认是正规用户自愿发送的请求
判断请求是否为正规用户自愿发送的实现方法,有如下3类:
1.嵌入机密信息(令牌)
如果在访问需要防范CSRF的页面时,提供第三方无法得知的机密信息的话,那么即使出现非正规用户自愿发送的请求,应用端也能够通过判断得知请求是否合法。称为令牌(Token)
例子:
在执行页面的前一个页面
<form action="xxx.php" method="POST">
新密码<input name="pwd" type="password"><br>
<input type="hidden" name="token" value="<?php
echo htmlspecialchars(session_id(), ENT_COMPAT, 'UTF-8');?>"> 嵌入令牌
<input type="submit" value="更改密码“ >
</form>
确认令牌(执行页面)
session_start()
if(session_id()!==$_POST['token']){ 确认令牌
die('请从正规页面进行操作'); //显示合适的错误信息
}
//下面进行关键处理
有一种令牌叫做一次性令牌,通常需要密码学级别的伪随机数生成器。
一次性令牌经常用于需要防范重放攻击(Reply Attack)的情况下。指,在监听得到加密的请求后,将该请求原封不动的再次发送而达到伪装的效果。一次性令牌能有效防御重放攻击。
并不推荐用一次性令牌当做防御CSRF的手段。
1.CSRF攻击与重放攻击毫不相干,因此并非一定要使用一次性令牌
2.没有证据说明一次性令牌比使用会话ID作为令牌的方法更安全
3.使用一次性令牌有时会导致正常的操作也出错
4.不能保证生成一次性令牌的方法一定安全
2.再次输入密码
除了用来防范CSRF,再次输入密码也被用于其他目的。
1.在用户下订单前,再次向用户确认购买意向
2.能够确认此时在电脑前操作的确实是用户本人
要求再次输入密码应在执行页面。
3.检验Referer
实例
if(preg_match('#\Ahttp://xxx.php#',@$_SWEVWE['HTTP_REFERER'])!==1){
die('请从正规页面进行操作‘);
}
检验Referer的方法也有缺陷。因为如果用户设置为不发送Referer,页面就无法正常显示。通过防火墙或浏览器插件等禁止Referer的用户不在少数。
另外,手机浏览器中也有不发送Referer的浏览器和能够关闭发送Referer功能的浏览器。
检验Referer时,必须要使用前方一质检所检验绝对URL,包括域名后的/
代码量最少,其他两种方法需要在2个页面中进行追加处理,而检验Referer方法只需要在执行关键处理的页面上追加处理即可。
嵌入令牌 | 再次输入密码 | 确认Referer | |
---|---|---|---|
开发耗时 | 中 | 中 | 小 |
对用户影响 | 无 | 增加了输入密码的麻烦 | 关闭了Referer的用户无法正常使用 |
能否用于手机网站 | 可 | 可 | 不可 |
建议使用的地方 | 最基本的防御策略,所有情况下均可使用 | 需要防范他人伪装或者确认需求很强的页面 | 用于能够限定用户环境的既有应用的CSRF防范策略 |
建议执行完关键处理后,向用户注册的邮箱发送有关处理内容的邮件,因为邮件是未经加密的明文传输,不要再邮件中添加重要信息。
对策总结
CSRF漏洞根本性防范策略如下
1.筛选出需要防范CSRF的页面
2.确认时正规用户自愿发起的请求
其中,确认请求确实由用户自愿发起的方法有以下三种。
1.嵌入机密信息(令牌)
2.再次输入密码
3.检验Referer
另外作为CSRF漏洞的辅助性对策,可以执行以下操作。
1.执行完关键处理后,向用户注册的邮箱发送通知邮件