zoukankan      html  css  js  c++  java
  • 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提供了三个可重新的虚方法AuthorizeCoreHandleUnauthorizedRequestOnAuthorization,那么在执行授权动作的过程中他们是如何被调用的呢?看下源码的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>

    Html.ActionLink,Html.Action等方法会将路由参数编码输出

    2)大部分的XSS攻击可通过对输入内容进行编码来阻止:Html.EncodeHtml.AttributeEncodeUrl.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.configcookie进行设置

    <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)使用UpdateModelTryUpdateModel

    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

    转载与引用请注明出处。
    
    时间仓促,水平有限,如有不当之处,欢迎指正。
  • 相关阅读:
    mysql 历史版本下载
    mysql 5.7 版本 You must reset your password using ALTER USER statement before executing this statement报错处理
    5.7 zip 版本的安装 以及遇到的坑
    mysql 5.6zip版本的卸载与5.7 zip 版本的安装
    mysql数据库的备份与还原
    本地Navicat连接docker里的mysql
    docker修改数据库密码
    docker 在push镜像到本地registry出现的500 Internal Server Error
    linux 没有界面内容显示不全解决办法
    json与map互相转换
  • 原文地址:https://www.cnblogs.com/hdwgxz/p/8637421.html
Copyright © 2011-2022 走看看