跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,一般缩写为 或者 XSRF, 是一种挟制用户在当前已登陆的Web应用程序上执行非本意的操做的攻击方法。[1] 跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。(维基百科)javascript
当咱们请求一个目标网站时,浏览器会发送该目标网站相关的cookie信息。当咱们浏览其余的网站时,若是咱们没有退出该目标网站,而其余的网站有一个向目标网站的操做连接,这时由于咱们在线,且有cookie信息,那么目标网站就会认为这是一个合法的请求而执行。可是这个操做不是咱们本身请求的,而是恶意者用咱们本身被认证过的身份执行的操做。html
这种恶意的网址并不须要一个特定的网站,它能够藏身在任何一个由用户生成内容的网站中,如论坛,博客等。java
若是有帐户名为Alice的用户访问了恶意站点,而她以前刚访问过在线交易网站,登陆信息还没有过时,那么她就会损失1000资金。浏览器
假设一个在线交易网站有一个转帐的url:cookie
http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName
一个恶意者在另外一个网站添加了以下代码session
<img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman">
//目标网站的form <form id="form" action="withdraw/trans" method="post"> <input name="account" type="text"/> <input name="amount" type="text"/> <input name="forAccount" type="text"/> </form>
恶意连接post
//指向恶意Html的连接 <img src="http://other/maliciouspage.html"/>
恶意html:网站
<html> <head> <script type="text/javascript"> window.onload = function () { document.myform.submit(); } </script> </head> <body> <form id="myform" name="myform" action="withdraw/trans" method="post"/> <input name="account" type="hidden" value="Alice" /> <input name="amount" type="hidden" value="1000"/> <input name="forAccount" type="hidden" value="Badman"/> </form> </body> </html>
恶意Html部署在http://other/maliciouspage.html
。恶意连接在任何一个能够由用户生成内容的网站,当有用户是Alice浏览到一个有恶意连接的网站时,就会加载恶意html,执行form方法。加密
如何避免这个恶意请求呢?url
既然浏览器是不可信的,那么就用一个非浏览器的而是跟页面自身(用户惟一)才有的惟一随机值做为验证对象。
生成验证值:
当在View中调用AntiForgeryToken
时会先生成一个cookie的名称,该名称的前缀为"_RequestVerificationToken"。
若是当前的请求中已有该cookie,则直接对该cookie的值进行序列化(包含加密解密操做)获得AntiForgeryData对象。若是没有该cookie则生成一个AntiForgeryData对象。
建立cookie,名称为1中生成的值,值为2中AntiForgeryData对象解析后的字符串(先后值会不同,由于还有其余的随机值做为解析参数)。
@Html.AntiForgeryToken
会生成一个名为"_RequestVerificationToken"的隐藏控件,该控件的值是根据现有的AntiForgeryData
对象再建立一个新的AntiForgeryData
对象,序列化的字符串。
匹配验证值:
在form提交时,服务端会先根据以前生成cookie的逻辑再次获取到cookie名称,若是没有找到对应cookie就抛出异常,若是找到该cookie,就解析解密为AntiForgeryToken对象。
而后在表单中查找名称为"_RequestVerificationToken"的控件,没有找到抛出异常,找到的话该控件的值解析解密为AntiForgeryToken对象.
隐藏控件中的AntiForgeryToken
和cookie解析解密的AntiForgeryToken
对象匹配,一致为合法,不一致为非法。
由于@Html.AntiForgeryToken
的值是随机且加密的,因此恶意html的表单里很难获取到正确的值。
Cookie:
HTTP/1.1 200 OK Cache-Control: private Set-Cookie: __RequestVerificationToken_L012Y0FwcDEx=EYPOofprbB0og8vI+Pzr1unY0Ye5BihYJgoIYBqzvZDZ+hcT5QUu+fj2hvFUVTTCFAZdjgCPzxwIGsoNdEyD8nSUbgapk8Xp3+ZD8cxguUrgl0lAdFd4ZGWEYzz0IN58l5saPJpuaChVR4QaMNbilNG4y7xiN2/UCrBF80LmPO4=; path=/; HttpOnly
Form:
<form action="..." method="post"> <input name="__RequestVerificationToken" type="hidden" value="yvLaFQ81JVgguKECyF/oQ+pc2/6q0MuLEaF73PvY7pvxaE68lO5qgXZWhfqIk721CBS0SJZjvOjbc7o7GL3SQ3RxIW90no7FcxzR6ohHUYEKdxyfTBuAVjAuoil5miwoY8+6HNoSPbztyhMVvtCsQDtvQfyW1GNa7qvlQSqYxQW7b6nAR2W0OxNi4NgrFEqbMFrD+4CwwAg4PUWpvcQxYA==" /> </form>
//Razor: <form id="form" action="withdraw/trans" method="post"> @Html.AntiForgeryToken() <input name="account" type="text"/> <input name="amount" type="text"/> <input name="forAccount" type="text"/> </form> //Controller:添加特性[ValidateAntiForgeryToken]且必须是HttpPost [HttpPost] [ValidateAntiForgeryToken] public ActinResult Trans(string account,double amount,string forAccount) { return View(); }
get请求最好是只读的,对于有操做的请求最好用post来实现。
参考: