ASP.NET MVC 使用防伪造令牌来避免CSRF攻击
本文转自这篇文章
XSRF即在访问B站点的时候,执行了A站点的功能。
比如:
A站点登录后,可以修改用户的邮箱(接口:/Email/Modify?email=123),修改邮箱时只验证用户有没有登录,而且登录信息是保存在cookie中。
用户登录A站点后,又打开一个窗口访问B站点,如果这时B站点内嵌入了一条链接http://www.A.com/Email/Modify?email=123,当用户点击这条链接时会直接修改A站点的用户邮箱。
对表达提交来说,要关注的就是安全问题。ASP.NET MVC 提供了探测某种攻击类型的机制,其中一个措施就是防伪造令牌。这种令牌包含服务器端和客户端组件,代码会在表单中插入一个隐藏域以保护用户特定的令牌:
@Html.AntiForgeryToken()
在执行@Html.AntiForgeryToken()语句时,会在cookie中写入一个经过加密后的数据,并在页面中添加一个隐藏域并写入加密后的数据(默认名称为__RequestVerificationToken)。当执行IndexPost(前面示例)方法前,会判断cookie中的数据与隐藏域的数据是否相等。相等则验证通过。否则会抛出异常。(Post请求会自动把隐藏域传递到后台,如果是Get请求,就需要手动把隐藏域的值传递到后台)。
待加密的数据是一个AntiForgeryToken对象。系统进行验证时,会先把加密的数据还原成AntiForgeryToken对象,对象有一个SecurityToken属性(用于填充随机序列),系统主要判断该字段的值是否相等。
同一个会话期间,SecurityToken数据相同,所以即使开多个tab访问相同页面,数据验证也会通过。
同一个会话期间cookie中的加密数据不会改变,因为访问页面时,cookie会传到后台,后台判断cookie中有加密数据,就不会重新生成cookie数据。但隐藏域的值每次都不同,因为每访问一次页面,都会重新加密一次,虽然AntiForgeryToken对象的值相同,但通过MachineKey的Protect加密后,每次加密的值都会不同。
AntiForgery使用MachineKey进行加密,所以如果系统使用负载均衡,就需要配置MachineKey,否则不同服务器的MachineKey不同,导致无法解密。
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model)
{//ect.
}
在执行@Html.AntiForgeryToken()语句时,会调用GetHtml方法。GetHtml方法中会调用GetFormInputElement方法,该方法会在cookie中写入加密后的数据,并返回Html标签代码。该标签代码会写入到页面中.
public static HtmlString GetHtml(){if (HttpContext.Current == null){throw new ArgumentException(WebPageResources.HttpContextUnavailable);}TagBuilder retVal = _worker.GetFormInputElement(new HttpContextWrapper(HttpContext.Current));return retVal.ToHtmlString(TagRenderMode.SelfClosing);}
在GetFormInputElement方法中,首先通过GetCookieTokenNoThrow方法获取Cookie中AntiForgeryToken对象(第一访问页面该对象为空)。再通过GetTokens方法获取新的newCookieToken以及formToken(newCookieToken就是写入cookie的token,formToken就是写入隐藏域的token)。如果oldCookieToken不为空,那么newCookieToken就会为空,这样就不会重新写入cookie。所以同一个会话期间cookie值会相同。如果不为空就通过SaveCookieToken方法写入cookie。
public TagBuilder GetFormInputElement(HttpContextBase httpContext){CheckSSLConfig(httpContext);AntiForgeryToken oldCookieToken = GetCookieTokenNoThrow(httpContext);AntiForgeryToken newCookieToken, formToken;GetTokens(httpContext, oldCookieToken, out newCookieToken, out formToken);if (newCookieToken != null){// If a new cookie was generated, persist it._tokenStore.SaveCookieToken(httpContext, newCookieToken);}if (!_config.SuppressXFrameOptionsHeader){// Adding X-Frame-Options header to prevent ClickJacking. See// http://tools.ietf.org/html/draft-ietf-websec-x-frame-options-10// for more information.httpContext.Response.AddHeader("X-Frame-Options", "SAMEORIGIN");}// <input type="hidden" name="__AntiForgeryToken" value="..." />TagBuilder retVal = new TagBuilder("input");retVal.Attributes["type"] = "hidden";retVal.Attributes["name"] = _config.FormFieldName;retVal.Attributes["value"] = _serializer.Serialize(formToken);return retVal;}
SaveCookieToken方法先通过Serialize方法序列化,序列化的时候会对数据加密,再写入cookie
public void SaveCookieToken(HttpContextBase httpContext, AntiForgeryToken token){string serializedToken = _serializer.Serialize(token);HttpCookie newCookie = new HttpCookie(_config.CookieName, serializedToken){HttpOnly = true};// Note: don't use "newCookie.Secure = _config.RequireSSL;" since the default// value of newCookie.Secure is automatically populated from the <httpCookies>// config element.if (_config.RequireSSL){newCookie.Secure = true;}httpContext.Response.Cookies.Set(newCookie);}
GetTokens方法,如果oldCookieToken不为空,就不重新生成newCookieToken。为空则通过GenerateCookieToken方法生成一个Token。再调用GenerateFormToken方法生成formToken
private void GetTokens(HttpContextBase httpContext, AntiForgeryToken oldCookieToken, out AntiForgeryToken newCookieToken, out AntiForgeryToken formToken){newCookieToken = null;if (!_validator.IsCookieTokenValid(oldCookieToken)){// Need to make sure we're always operating with a good cookie token.oldCookieToken = newCookieToken = _validator.GenerateCookieToken();}Contract.Assert(_validator.IsCookieTokenValid(oldCookieToken));formToken = _validator.GenerateFormToken(httpContext, ExtractIdentity(httpContext), oldCookieToken);}
GenerateCookieToken方法生成cookieToken,即创建一个新的AntiForgeryToken对象。AntiForgeryToken有个SecurityToken属性,类型为BinaryBlob。BianryBlob对象会通过RNGCryptoServiceProvider实例的GetBytes方法填充强随机序列。填充的序列就是用来验证的随机数。即随机数是在创建AntiForgeryToken对象时自动生成的
public AntiForgeryToken GenerateCookieToken(){return new AntiForgeryToken(){// SecurityToken will be populated automatically.IsSessionToken = true};}
GenerateFormToken方法,就是把cookieToken的SecurityToken赋值给formToken。这样就会使得cookieToken与formToken的SecurityToken值相等
public AntiForgeryToken GenerateFormToken(HttpContextBase httpContext, IIdentity identity, AntiForgeryToken cookieToken){Contract.Assert(IsCookieTokenValid(cookieToken));AntiForgeryToken formToken = new AntiForgeryToken(){SecurityToken = cookieToken.SecurityToken,IsSessionToken = false};bool requireAuthenticatedUserHeuristicChecks = false;// populate Username and ClaimUidif (identity != null && identity.IsAuthenticated){if (!_config.SuppressIdentityHeuristicChecks){// If the user is authenticated and heuristic checks are not suppressed,// then Username, ClaimUid, or AdditionalData must be set.requireAuthenticatedUserHeuristicChecks = true;}formToken.ClaimUid = _claimUidExtractor.ExtractClaimUid(identity);if (formToken.ClaimUid == null){formToken.Username = identity.Name;}}// populate AdditionalDataif (_config.AdditionalDataProvider != null){formToken.AdditionalData = _config.AdditionalDataProvider.GetAdditionalData(httpContext);}if (requireAuthenticatedUserHeuristicChecks&& String.IsNullOrEmpty(formToken.Username)&& formToken.ClaimUid == null&& String.IsNullOrEmpty(formToken.AdditionalData)){// Application says user is authenticated, but we have no identifier for the user.throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,WebPageResources.TokenValidator_AuthenticatedUserWithoutUsername, identity.GetType()));}return formToken;}
生成cookieToken和formToken后就会调用Serialize方法进行序列化。序列化的时候会调用MachineKey的Protect方法进行加密。每次加密后的值都不相同。如果使用了负载均衡,一定要配置MachineKey,而不能使用系统的值
public string Serialize(AntiForgeryToken token){Contract.Assert(token != null);using (MemoryStream stream = new MemoryStream()){using (BinaryWriter writer = new BinaryWriter(stream)){writer.Write(TokenVersion);writer.Write(token.SecurityToken.GetData());writer.Write(token.IsSessionToken);if (!token.IsSessionToken){if (token.ClaimUid != null){writer.Write(true /* isClaimsBased */);writer.Write(token.ClaimUid.GetData());}else{writer.Write(false /* isClaimsBased */);writer.Write(token.Username);}writer.Write(token.AdditionalData);}writer.Flush();return _cryptoSystem.Protect(stream.ToArray());}}}
ASP.NET MVC 使用防伪造令牌来避免CSRF攻击相关推荐
- 我要学ASP.NET MVC 3.0(十三): MVC 3.0 防止跨站点请求伪造 (CSRF) 攻击
我要学ASP.NET MVC 3.0(十三): MVC 3.0 防止跨站点请求伪造 (CSRF) 攻击 概述 众所周知,ASP.Net MVC程序在浏览器运行时产生了标准的Html标签,包括 ...
- ASP.NET MVC 5 - 验证编辑方法(Edit method)和编辑视图(Edit view)
在本节中,您将验证电影控制器生成的编辑方法(Edit action methods)和视图.但是首先将修改点代码,使得发布日期属性(ReleaseDate)看上去更好.打开Models \ Movie ...
- 认识ASP.NET MVC的5种AuthorizationFilter
在总体介绍了筛选器及其提供机制(<深入探讨ASP.NET MVC的筛选器>)之后,我们按照执行的先后顺序对四种不同的筛选器进行单独介绍,首先来介绍最先执行的AuthorizationFil ...
- ASP.NET MVC中的安全性
目录 介绍 认证 表单身份验证 Windows身份验证 如何配置表单身份验证? 我们如何使用Windows身份验证进行身份验 XSS Anti XSS Library 跨站点请求伪造 那问题是什么? ...
- asp html表单没有csrf保护,ASP.NET MVC 和网页中的 XSRF/CSRF 防护
ASP.NET MVC 和网页中的 XSRF/CSRF 防护 03/14/2013 本文内容 跨站点请求伪造(也称为 XSRF 或 CSRF)是一种针对 Web 托管型应用程序的攻击,恶意网站凭此可以 ...
- Asp.net MVC 3 防止 Cross-Site Request Forgery (CSRF)原理及扩展 安全 注入
原理:http://blog.csdn.net/cpytiger/article/details/8781457 原文地址:http://www.cnblogs.com/wintersun/archi ...
- 成员资格、授权 – ASP.NET MVC 4 系列
ASP.NET MVC 不像 ASP.NET WEB FORMS 那样提供了很多自动保护机制来保护页面不受恶意用户的攻击,更明确的说,后者是致力于使应用程序免受攻击: 服务器组件对显示的值和特性进行 ...
- apache2.4.9 开启path_info访问_【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6)...
新建项目 打开VS2015,找到菜单项[文件->新建->项目],打开向导对话框: 注意我们的选择项: 运行平台:.NET FrameWork 4.5 项目模板:ASP.NET Web Ap ...
- 《Pro ASP.NET MVC 3 Framework》学习笔记之五【依赖注入及ninject工具使用】
一,创建松耦合的组件 1."分解关注点"是MVC模式里面一个非常重要的特性.我们想要在应用程序里面创建的组件尽可能的独立,这样我们就能管理比较少的依赖关系.理想情况下,每个组件都是 ...
最新文章
- cocos2dx对所有子节点设置透明度
- 清华大学图神经网络综述:模型与应用
- 根据json文件读取json信息
- js高级—tab栏切换(面向对象做法)
- 光纤接口怎么接 图解_光纤的数据比网线快很多倍,但为什么没有在家庭局域网中普及呢?...
- 如何学习前端 转载
- mysql5.5中的MaxValue关键字
- Linux 下几款程序内存泄漏检查工具
- node本地连接服务器的数据库_Linux本地连接阿里云服务器,以及下载node.js配置环境...
- vue监听路由的变化,跳转到同一个页面时,Url改变但视图未重新加载问题
- POJ 3308 最少点集覆盖
- Matlab 2016 超详细安装教程
- 博弈论python实例_博弈论读书笔记(七)贝叶斯博弈举例和显示原理
- 使用NetMHCpan进行肿瘤新抗原预测分析
- linux 内核usleep,linux 内核 usleep
- 中小型电子商务网站架构
- 用VB开发USB接口POS打印机进行打印和弹钱箱
- python应用——将raw文件转化为jpg文件,并显示图像
- 【you may need to restart the kernel to use updated packages】
- matlab语音转换文字,语音转换 有没有做这方面的大侠,大家一起来交流一下经验...