跨站请求伪造(CSRF)
一个跨站请求伪造攻击迫使登录用户的浏览器将伪造的HTTP请求,包括受害者的会话cookie和所有其他自动填充的身份认证信息,发送到一个存在漏洞的web应用程序。这种攻击允许攻击迫使受害者的浏览器生成让存在漏洞的应用程序认为是受害者的合法请求的请求。
我存在CSRF漏洞?
检测应用程序是否存在该漏洞的方法是查看是否每个链接和表单都提供了不可预测的CSRF令牌。没有这样的令牌,攻击者就能够伪造恶意请求。另一种防御的方法是要求用户证明是他们要提交请求,如重新认证。
重点关注那些调用能够改变状态功能的链接和表单,因为他们是跨站请求伪造攻击的最重要的目标。多步交易并不具备内在的防攻击能力,并且攻击者也可以利用SSRF来诱导应用程序和API来产生任意的HTTP请求。
请注意:会话cookie、源IP地址和其他浏览器自动发送的信息不能作为防攻击令牌,因为这些信息已经包含在伪造的请求中。
攻击案例场景
应用程序允许用户提交不包含任何保密字段的状态改变请求,如:
http://example.com/app/transferFunds?amount=1500&destinationAccount=4673243243
因此,攻击者构建一个请求,用于将受害用户账户中的现金转移到自己账户。然后攻击者在其控制的多个网站中以图片请求或iframe中嵌入这种攻击。
<imgsrc="http://example.com/app/transferFunds?amount=1500&destinationAccount=attackersAcct#“width="0" height="0" />
如果受害用户通过了example.com认证,则伪造的请求将自动包含用户的会话信息,授权执行攻击者的请求。
我如何防止CSRF?
优先考虑的就是利用已有的CSRF防护方案。许多框架,如Spring, Play, Django以及AngularJS,都内嵌了CSRF防护,一些web开发语言如.Net也提供了类似的防护。OWASP CSRF Guard对Java应用提供了CSRF防护,OWASPCSRFProtector对PHP应用和Apache server也提供CSRF了防护。否则需要在每个HTTP请求中添加一个不可预测的令牌,这种令牌至少应该对每一个用户会话来说是唯一的。
1.最好的方法是将独有的令牌包含在一个隐藏字段中。这将使得该令牌通过HTTP请求体发送,避免其包含在URL中从而被暴露出来。
2.将令牌放在URL中或作为一个URL参数,但是这种方法的风险在于令牌会暴露给攻击者。
3.考虑对所有cookie使用”SameSite=strict”标签,该标签逐渐被各类浏览器所支持。
常用防止方法(ASP为例)
在共用页面创建一个产生随机字符串的方法,再在表单页面创建一个token,在form表单中建立一个隐藏字段,用来将token值传递给action中的页面,要跳转的页面接受到传递过来的token进行验证。
注:不是所有的表单都适合用这样的方法,但是绝大多数可以,像跳转成功后的页面,有频繁的刷新操作,这时传过来的token值就没有值,校验不成功,这时,处理就比较难处理,所以在用时具体分析而定。
1、创建一个随机字符串
Function randKey(obj)
Dim char_array(80)
Dim temp
For i = 0 To 9
char_array(i) = Cstr(i)
Next
For i = 10 To 35
char_array(i) = Chr(i + 55)
Next
For i = 36 To 61
char_array(i) = Chr(i + 61)
Next
Randomize
For i = 1 To obj
'rnd函数返回的随机数在0~1之间,可等于0,但不等于1
'公式:int((上限-下限+1)*Rnd+下限)可取得从下限到上限之间的数,可等于下限但不可等于上限
temp = temp&char_array(int(62 - 0 + 1)*Rnd + 0)
Next
randKey = temp 'N2fIgeYvD0mVyUPejm8ZeYtVi1iTgloWjMAHFZJeudQlaoT9xErIz03CvYmRL33
End Function
2、创建token
创建一个token,值等于随机字符串
session("CsrfToken")=randKey("64")
3、表单中隐藏的token值,用来将token传递给下一个页面,下一个页面接受到传递过来的token进行验证
<input name="eToken_DSI" type="hidden" value="<%=session("CsrfToken") %>" />
4、 接收到的token值,与session中的值进行比对,一样则可以跳转,不一样则重定向页面
if Request("eToken_DSI") <> session("CsrfToken") or IsEmpty(session("CsrfToken")) then
response.Write "<script>window.history.go(-1);</script>"
response.End
else
'response.Write "true"
end if
session("CsrfToken")=""