什么是CSRF(跨站攻击)
可能很多人已经对CSRF有所了解,就简单的介绍下:
CSRF全程是 Cross-Site Request Forgery 。大概意思就是在登录用户不知情的情况下,由一个网站对已经登录的另外一个网站发送请求。
为了保证请求是来自我们自己的网站,在表单的隐藏域中添加一个唯一的值,这个值被成为 AntiForgeryToken。
如果你想详细的了解下CSRF,你可以看下百度百科。
CSRF和Nancy
正如Nancy的其他功能,你不可能在默认情况下就能获得一个完整的解决方案。由于Nancy是轻量级、自由开发的,我们还是需要付出一点努力才能使用CSRF防护。
- 在Bootstrapper中启用CSRF防护支持
- 在应用的每一个Form中添加AntiForgeryToken
- 使用Nancy内置的方法ValidateCsrfToken验证CSRF票据
- 处理Nancy无法接收正确票据的请求
启用CSRF
如果没有创建就创建一个NancyDefaultBootstrapper类,添加下面的代码到ApplicationStartup()方法中
Nancy.Security.Csrf.Enable(pipelines);
我们的Bootstrapper如下:
using Nancy; using Nancy.Bootstrapper; using Nancy.TinyIoc; namespace NancyCsrfDemo { public class Bootstrapper : DefaultNancyBootstrapper { protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) { base.ApplicationStartup(container, pipelines); Nancy.Security.Csrf.Enable(pipelines); } } }
在表单中添加AntiForgeryToken
如果你采用的是Razor视图引擎,视图文件应该是下面的样子:
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title></title> </head> <body> <h1>Welcome to Nancy CSRF Demo, @ViewBag.Name</h1> <form action="/" method="POST"> Name:<br/> <input type="text" name="name" value="@ViewBag.Name" /> <input type="submit" /> @Html.AntiForgeryToken() </form> </body> </html>
如果采用的是SSVE (The Super Simple View Engine)
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title></title> </head> <body> <h1>Welcome to Nancy CSRF Demo, @Model.Name</h1> <form action="/" method="POST"> Name:<br/> <input type="text" name="name" value="@Model.Name" /> <input type="submit" /> @AntiForgeryToken </form> </body> </html>
不管使用那个引擎,都会在表单中生成一个隐藏域
<input type="hidden" name="NCSRF" value="AAEAAAD/////AQAAAAAAAAAMAgAAAD1OYW5jeSwgVmVyc2lvbj0wLjIyLjIuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsBQEAAAAYTmFuY3kuU2VjdXJpdHkuQ3NyZlRva2VuAwAAABw8UmFuZG9tQnl0ZXM+a19fQmFja2luZ0ZpZWxkHDxDcmVhdGVkRGF0ZT5rX19CYWNraW5nRmllbGQVPEhtYWM+a19fQmFja2luZ0ZpZWxkBwAHAg0CAgAAAAkDAAAAOrVPNrYs0YgJBAAAAA8DAAAACgAAAAI4qmisW7sYu2FlDwQAAAAgAAAAAsdnVjuuU1fzpb58LEjF1pcK98u9ENMjG0viyxLpvlv/CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="/>
校验Token
当用户点击按钮的时候,浏览器就会向后台发送 AntiForgeryToken/CSRF 票据。
Nancy有一个内置方法ValidateCsrfToken,这个方法会自动检测隐藏域Request.Form.Ncsrf,如果票据不对会报错异常:CsrfValidationException
异常会默认返回一个500错误,我们需要捕捉这个异常,并进行一些处理:
using Nancy; using Nancy.Security; namespace NancyCsrfDemo { public class HomeModule : NancyModule { public HomeModule() { Get["/"] = p => { return View["Index"]; }; Post["/"] = p => { try { this.ValidateCsrfToken(); } catch (CsrfValidationException) { return Response.AsText("Csrf Token not valid.").WithStatusCode(403); } ViewBag.Name = Request.Form.Name; return View["Index"]; }; } } }
这样如果票据没有验证通过的话,用户就会收到一个 403禁止访问错误,而不是500错误。