ASP.NET MVC编程——验证、授权与安全

1 验证

一般采用表单验证完成登陆验证,建议结合SSL使用。为限制控制器只能执行HTTPS,使用RequireHttpsAttribute

2 授权

对账户的权限的控制可以通过在控制器或控制器操作上加AuthorizeAttribute 属性。

扩展授权过滤器

扩展授权过滤器可以定义继承自AuthorizeAttribute的类,也可以定义同时继承自FilterAttribute, IAuthorizationFilter接口的类。

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
    {
        public AuthorizeAttribute(); 

        // 获取或设置有权访问控制器或操作方法的用户角色
        public string Roles { get; set; }
        
        //获取此特性的唯一标识符。
        public override object TypeId { get; }

        // 获取或设置有权访问控制器或操作方法的用户。
        public string Users { get; set; }

        //重写时,提供一个入口点用于进行自定义授权检查
        // 返回结果: 如果用户已经过授权,则为 true;否则为 false。
        // 异常:System.ArgumentNullException:httpContext 参数为 null。
        protected virtual bool AuthorizeCore(HttpContextBase httpContext);

        //处理未能授权的 HTTP 请求。
        protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext);

        //在过程请求授权时调用。
        // 异常: System.ArgumentNullException:
        //filterContext 参数为 null。
        public virtual void OnAuthorization(AuthorizationContext filterContext);
        
        // 返回结果: 对验证状态的引用。
        // 异常:System.ArgumentNullException:
        // httpContext 参数为 null。
        protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext);
}

AuthorizeAttribute提供了三个可重新的虚方法AuthorizeCore,HandleUnauthorizedRequest,OnAuthorization,那么在执行授权动作的过程中他们是如何被调用的呢?看下源码的OnAuthorization方法,发现在这个方法中先调用AuthorizeCore,然后调用HandleUnauthorizedRequest被调用了。

        public virtual void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            //如果子操作的缓存处于活动状态,那么就抛出异常
            if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
            {
                throw new InvalidOperationException(MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache);
            }

            //判断控制器或控制器操作是否允许匿名访问,如果可以就return
            bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)|| filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true);

            if (skipAuthorization)
            {
                return;
            }

            //进行权限验证
            if (AuthorizeCore(filterContext.HttpContext))
            {
                HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
                cachePolicy.SetProxyMaxAge(new TimeSpan(0));
                cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
            }
            else
            {//处理未通过权限验证的情形
                HandleUnauthorizedRequest(filterContext);
            }
        }

当子操作缓存处于活动状态,那么抛出异常。然后检验是否可匿名访问,如果可以匿名访问就不进行验证;

综合以上分析,扩展AuthorizeAttribute要注意:

1)在子类AuthorizeCore中,调用父类的AuthorizeCore方法

base.OnAuthorization(filterContext);

2)在子类的AuthorizeCore方法中验证用户的权限。

3)通过子类的构造函数传入用户的权限值

代码示例如下:

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
        private UserRole role;
        public CustomAuthorizeAttribute(UserRole role)
        {
            this.role = role;
        }
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            bool ret = false;

            //获得用户信息(从本地Session或分布式缓存中获取)
            var userInfo = ......
            if(userInfo==null)
            {
                //信息为null,一般认为登陆超时或没有登陆
            }

            if(userInfo.Role == UserRole.Org)
            {
                ret = true;
            }
            else
            {
                //提示无权限
            }

            return ret;
        }
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {//针对ajax请求进行处理
                
            }
            else
            {//非aiax进行处理
                
                //跳转到指定页面
                string strUrl = ......;
                filterContext.Result = new RedirectResult(strUrl);
            }
        }
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
        }
    }
    public enum UserRole
    {
        Org = 1,
        Vip = 2,
        Guest = 3
}

3 安全

总的原则:

所有层或各个子系统各自负责好自己的安全。

任何用户数据和来自其他系统的数据都要经过检验。

在满足需求的情况下,尽量缩小账户的权限。

减少暴露的操作数量和操作参数。

关闭服务器不需要的功能。

4 防范攻击

4.1跨站脚本攻击(XSS)

被动注入:用户的输入含有恶意脚本,而网站又能够不加检验地接受这样的输入,进而保存到数据库中。

主动注入:用户将含有恶意脚本的内容输入到页面文本框中,然后在屏幕上显示出来。

防御方法:

1)使用Razor语法输出的内容已经被编码,可以不做任何其他处理

例如:

<h4>@Model.Field</h4>

2)大部分的XSS攻击可通过对输入内容进行编码来阻止:Html.Encode,Html.AttributeEncode,Url.Encode

3)对Js进行编码

使用Ajax.JavaScriptStringEncode

4)将AntiXSS库作为默认的编码器(不建议使用,不灵活)

ASP.NET 4.5 集成Anti-XSS Library,可以通过配置来对整个网站的输出进行编码。

<system.web>
    <httpRuntime targetFramework="4.5" encoderType="System.Web.Security.AntiXss.AntiXssEncoder,System.Web"/>
</system.web>

4.2跨站请求伪造(CSRF/XSRF)

防御方法:

1)使用Html隐藏域存储用户令牌,令牌可以存储在Session里或者cookie里

2)在视图表单中使用@Html.AntiForgeryToken(),在控制器操作上添加属性[ValidateAntiForgeryToken],注意表单一定要使用@Html.BeginForm生成

实现机制:AntiForgeryToken方法向用户浏览器cookie中写入一个加密的数据,并在表单内插入一个隐藏栏位,每次刷新页面时隐藏栏位的值都不同,每次执行控制器操作前,都会验证隐藏栏位和浏览器cookie中的值是否相同,只有相同才允许执行控制器操作。

使用限制:

  • 客户端浏览器不能禁用cookie
  • 只对post请求有效
  • 若有XSS漏洞,则可轻易获取令牌
  • 对Ajax请求不能传递令牌,即对Ajax无效

3)使用幂等的Get请求,仅使用Post请求修改数据(仅仅是一定程度上限制这种攻击而已)

4)使用动作过滤器,验证UrlReferrer

扩展的动作过滤器:

public class CSRFFilter:AuthorizeAttribute
{
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext == null)
            {
                throw new HttpException("请求无效");
            }

            if (filterContext.HttpContext.Request.UrlReferrer == null)
            {
                throw new HttpException("请求无效");
            }

            if (filterContext.HttpContext.Request.UrlReferrer.Host != "sit.com")
            {
                throw new HttpException("来自非法网站");
            }
        }
}

4.3 cookie盗窃

cookie有两种形式

1)会话cookie:存储在浏览器内存中,浏览器每次请求通过Http头进行传递

2)持久性cookie:存储在硬盘上,同样通过Http头进行传递

二者的区别:会话cookie常在会话结束时失效,而持久性cookie在下一次访问站点时仍然有效。

被窃取的原因:依赖于XSS漏洞,注入一段恶意脚本就能窃取。

防御方法:

1)在web.config对cookie进行设置

<httpCookies httpOnlyCookies="true"/>,httpOnlyCookies指定为true表达仅服务器可以访问,浏览器无法访问

2)在编写代码时为每个cookie单独设置

Response.Cookies["cok"].Value = Guid.NewGuid().ToString();

Response.Cookies["cok"].HttpOnly = true;

4.4重复提交

防御方法:

1)使用bind特性,设置想要绑定的属性来,防止这种攻击。也可以设置不要绑定的字属性,但优先选择设置要绑定的属性。

例:

可以指定多个字段,用逗号分隔

public ActionResult TestViewData([Bind(Include = "Field,Field1,Field1")]ModelF mf)
{
     ......
}

2)使用UpdateModel或TryUpdateModel

3)使用ViewModel,明确规定View使用的数据模型

4.5开放重定向

防御方法:

使用Url.IsLocalUrl检测是否为本地url

4.6 SQL注入攻击

防御方法:

通过参数注入非法获得或修改网站数据。

使用参数化查询来防止SQL注入攻击。

参考:

1.Jess Chadwick/Todd Snyder/Hrusikesh Panda,徐雷/徐扬

译。ASP.NET MVC4 Web编程

2.Jon Galloway/Phil Haack/Brad Wilson/K. Scott Allen,孙远帅/邹权译  ASP.NET MVC4 高级编程(第四版)

3.黄保翕,ASP.NET MVC4开发指南

4.蒋金楠,ASP.NET MVC4框架揭秘

5.https://www.asp.net/mvc

-----------------------------------------------------------------------------------------

转载与引用请注明出处。

时间仓促,水平有限,如有不当之处,欢迎指正。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏技术博客

Asp.Net Mvc3.0(MEF依赖注入实例)

在http://www.cnblogs.com/aehyok/p/3386650.html前面一节主要是对MEF进行简单的介绍。本节主要来介绍如何在Asp.Ne...

902
来自专栏葡萄城控件技术团队

Asp.Net MVC4入门指南(8):给数据模型添加校验器

在本节中将会给Movie模型添加验证逻辑。并且确保这些验证规则在用户创建或编辑电影时被执行。 保持事情 DRY ASP.NET MVC 的核心设计信条之一是DR...

18810
来自专栏坚毅的PHP

jersey处理支付宝异步回调通知的问题:java.lang.IllegalArgumentException: Error parsing media type 'application/x-www

tcpflow以流为单位分析请求内容,非常适合服务器端接口类服务查问题 这次遇到的问题跟支付宝支付后的回调post结果有关 淘宝的代码例子: publi...

5995
来自专栏运维一切

laravel自定义错误页面 原

app\Exceptions\handler.php 在render的时候就携带了这个异常

1123
来自专栏Jed的技术阶梯

zookeeper编程01-循环监听

客户端发起对节点的事务操作(以NodeChildrenChanged事件为例) 服务端监听到对应的事件后进行相应的操作

3832
来自专栏程序你好

不同的.Net版本客户端软件调用Java Web Service区别

最近的系统中需要.Net开发的离线端软件通过Web Service技术和Java开发的在线系统进行数据交互。

1123
来自专栏智能大石头

NewLife.Redis基础教程

X组件缓存架构以ICache接口为核心,包括MemoryCache、Redis和DbCache实现,支持FX和netstandard2.0! 后续例程与使用说明...

1053
来自专栏软件工程师成长笔记

用MINA实现UDP通信的例子

Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP、UDP/IP协议栈的通信框架(当然,也可以提供JAVA 对象...

1242
来自专栏黑泽君的专栏

SolrCloud搭建 + zookeeper集群搭建 + 搜索功能切换到集群版 + httpclient学习 + 全局异常处理器

索引集合包括两个Shard(Shard1和Shard2),Shard1和Shard2分别由三个Core组成,其中一个Leader两个Replication,L...

1792
来自专栏恰童鞋骚年

设计模式的征途—19.命令(Command)模式

在生活中,我们装修新房的最后几道工序之一是安装插座和开关,通过开关可以控制一些电器的打开和关闭,例如电灯或换气扇。在购买开关时,用户并不知道它将来到底用于控制什...

612

扫码关注云+社区

领取腾讯云代金券