• 成员资格、授权 – ASP.NET MVC 4 系列


           ASP.NET MVC 不像 ASP.NET WEB FORMS 那样提供了很多自动保护机制来保护页面不受恶意用户的攻击,更明确的说,后者是致力于使应用程序免受攻击:

    1. 服务器组件对显示的值和特性进行 HTML 编码,以帮助阻止 XSS 攻击。
    2. 加密和验证试图状态,从而帮助阻止篡改提交的表单。
    3. 请求验证(%@page validaterequest="true"%)截获看起来是恶意的数据,并给出警告(也是 ASP.NET MVC 框架默认开启的保护)。
    4. 事件验证帮助组织注入攻击和提交无效值。

          ASP.NET MVC 对标记和程序的运行提供了更多控制,这意味着程序员要承担更多的责任。

           之所以应用程序存在安全隐患,主要是因为开发人员缺乏足够的信息或理解。另外,人无完人,难免有疏忽的时候,鉴于此,下面是本章的关键总结:

    1. 永远不要相信用户提供的数据。
    2. 每当渲染作为用户输入而引入的数据时,要进行 HTML 编码;如果数据作为特性值显示,要进行 HTML 特性编码(HTML-attribute-encode)。
    3. 考虑网站哪些部分允许匿名访问,哪些部分要求认证访问。
    4. 在不需要通过客户端脚本(大部分情况下)访问 cookie 时,使用 HTTP-only cookie。
    5. 记住,外部输入不是显式的表单域,因为它包括 URL 查询字符串、隐藏表单域、Ajax 请求以及我们使用的外部 Web 服务结果等。
    6. 强烈建议使用 AntiXSS 库(www.codeplex.com/AntiXSS)。

           黑客、解密高手、垃圾邮件发送者、病毒、恶意软件,它们都想进入计算机并查看或破坏里面的数据!

    使用 Authorize 特性登录

           保护应用程序的第一步,也是最简单的一步,就是要求登录系统的用户访问那些由应用程序指定的 URL。我们可以通过控制器上或控制器内部特定操作上的 Authorize 操作过滤器来实现。

           Authorize 特性是 ASP.NET MVC 自带的默认授权过滤器,可限制用户对操作方法的访问,若该特性运用于控制器,则会应用于控制器内部所有操作方法。

           有时会对用户身份验证和用户授权之间的区别感到困惑,这两个词也比较相似!

    • 身份验证:指通过使用登录机制的一些表单(包含用户名 / 密码、OpenID 等)核实用户的身份,即知道他是谁。
    • 授权验证:指用来核实该用户是否在他的权限内进行操作,即他能做什么,不能做什么。

           授权特性不带任何参数,只要求用户以某种角色身份登录网站,换句话说,禁止匿名访问!

    保护控制器操作

           现在根据一个非常简单的购物应用需求,创建音乐商店的应用程序。程序中的 StoreController 控制器仅包含 2 个操作,Index 和 Buy:

    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Mvc;
    using MvcMusicStore.Models;
     
    namespace MvcMusicStore.Controllers
    {
        public class StoreManagerController : Controller
        {
            public ActionResult Index()
            {
                var albums = GetAlbums();
                return View(albums);
            }
     
            [Authorize]
            public ActionResult Buy(int id)
            {
                var album = GetAlbums().Single(e => e.AlbumId == id);
                return View(album);
            }
     
            private static List<Album> GetAlbums()
            {
                var albums = new List<Album> { 
                    new Album {AlbumId = 1, Title = "The Fall of Math", Price = 8.99M},
                    new Album {AlbumId = 2, Title = "The Blue Notebooks", Price = 8.99M},
                    new Album {AlbumId = 3, Title = "Lost in Translation", Price = 9.99M},
                    new Album {AlbumId = 4, Title = "Permutation", Price = 10.99M}
                };
                return albums;
            }
        }
    }

           如果现在访问 Store 控制器的 Buy 操作时,就会要求登录。

           下面内容非常重要,请慢读、理解、记忆:

    • 使用 Web Forms 保护应用程序安全的一个普遍方法是使用 URL 授权。如果系统有管理模块并且限制只有 Admins 角色才能访问该模块,假设把所有管理页面都放在了 Admin 文件夹下,那么除了那些 Admins 角色外,其余用户一概禁止访问 Admin 子文件夹,这可以在网站的 web.config 文件中锁定这个目录,以保护其不被非法访问。
      <location path="Admin" allowOverride="false" />
      <system.web>
        <authorization>
          <allow roles="Administrator"/>
          <deny users="?"/>
        </authorization>
      </system.web>

           这种方式在 MVC 框架中无法正常工作,原因有两个。首先,请求不再映射到屋里目录;其次,可能存在多种查找同一控制器的方式。

    • 理论上,MVC 可以拥有一个封装了程序管理功能的 AdminController,然后在根目录 web.config 设置 URL 授权来阻止所有以 /Admin 开头的访问请求。但这未必是安全的,假设今后要切换 {controller} 和 {action} 的顺序,那先前的设置将不能阻止对这个 URL 的访问。

           实现安全性的最好方法是,安全性检查尽可能的接近要保护的对象。可能有高于堆栈的检查,但最终都要确保实际资源的安全。这样无论用户如何获得资源,该方式都会对其进行安全性检查。于是,也就不必依赖路由和 URL 授权来确保控制器安全了。

           Authorize 特性就起这个作用:

    • 如果不指定调用方法的角色和用户,就必须另外简单的验证当前用户。
    • 用户访问这个特性的操作方法,在授权检查失败的情况下,过滤器会引发服务器返回一个“401 未授权”的 HTTP 状态码。
    • 如果在 web.config 中启用了表单身份验证并指定了登录 URL 地址,那么 ASP.NET 会处理这个响应码,并将用户重定向。

    Ahthorize 特性在表单身份验证和 AccountController 控制器中的用法

           上面例子在后台是如何操作的呢?原来,在 ASP.NET MVC 的 InternetApplication 模板包含一个基本的 AccountController,它支持 ASP.NET Membership 和 OAuth 验证的账户管理。

           Authorize 特性是一个过滤器,它能先于控制器操作执行。首先会执行它在 OnAuthorization 方法中的主要操作,这是一个在接口 IauthorizationFilter 中定义的标准方法,查看源码就会发现,基本的安全机制正在核实 ASP.NET 上下文中存储的基本身份验证信息:

    return HttpContext.User.Identity.IsAuthenticated;

           如果用户验证失败,就会返回一个 HttpUnauthorizedResult 操作结果,产生一个 HTTP 401(未授权)的状态码。这个状态码被 FormsAuthenticationModule 的 Onleave 方法截获,并转而重定向到配置中的登录页面。

    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" timeout="2880" />
    </authentication>

           MVC 框架 InternetApplication 模板提供的这种方式,在简单的应用场合中可以轻松添加授权,而不需要编写任何额外代码及配置。

    整个控制器的安全

           有时可能会希望授权级别是控制器,而不是在内部每一个操作上添加 Authorize 特性。此时,可以添加 Authorize 特性至 Controller 上。

    使用全局授权过滤器保护整个应用程序安全

           大部分网站,基本上整个应用程序都是需要授权的。这种情形下,默认授权要求和匿名访问少数网页就变得极其简单。把 AuthorizeAttribute 配置为全局过滤器,而使用 AllowAnonymous 特性标注允许匿名访问的控制器或方法。

    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new AuthorizeAttribute());
            filters.Add(new HandleErrorAttribute());
        }
    }

           这样就限制了对整个应用程序所有操作的访问,但也别忘了标注允许匿名访问的控制器或操作(如果有的话)。

    角色成员使用 Authorize 特性

    [Authorize(Roles = "Administrator")]
    public class StoreManagerController : Controller

           这样就限定了授权访问的用户角色只能是管理员角色。顾名思义(Roles 是复数),传递的角色列表可以是逗号间隔的字符串;也可以授权给一组用户 Users = "xx,xxx";也可以两者同时使用。

           当然,应当使用角色而非用户组。另外,当创建角色组时,可考虑使用基于特权的角色分组,像 CanEditAlbums 这样的角色组远比 SuperAdmin、CEOOffice更为精细,更为便于管理。

    扩展角色和成员

           ASP.NET MVC 的好处之一就是它运行在成熟且功能齐全的 ASP.NET 核心之上。而 ASP.NET MVC 中的身份验证和授权建立在 System.Web.Security 命名空间中的 Role 类和 Membership 类之上。这样做是有好处的:

    1. 可使用基于 ASP.NET Membership 系统的现有代码和技术。
    2. 通过使用 ASP.NET Membership 和 Roles 的 API,可以扩展来处理安全性问题的 ASP.NET MVC 组件(如授权和默认的控制器 AccountController)。
    3. 可利用提供器系统创建自己的 Membership、Role 和 Profile 提供器来配合 ASP.NET MVC 工作。

    通过 OAuth 和 OpenID 的外部登录

           运用本地数据库维护用户信息也有一些严重的负面影响:

    1. 维护包含用户信息和加密口令的本地数据库是一项重大的责任!重大安全漏洞、用户信息泄漏在当今已司空见惯,甚至一些用户各网站密码是相同的。
    2. 网站注册非常麻烦。用户已经厌倦了填写各种表格,适用各种不同的密码规则、记忆密码以及担心网站是否能确保他们的信息安全。

           OAuth 和 OpenID 是开放的授权标准。这些协议允许用户使用他们已有的账户登录我们的网站,这些账户来自于他们信任的网站(提供器)。过去,配置网站以支持 OAuth 和 OpenID 是非常难以实现的。原因有以下两点:首先是协议复杂,然后是顶级提供器对这两种协议的实现方式不一样。

           MVC 4 通过在 Internet 模板中内置支持 OAuth 和 OpenID 极大化的简化了这一点。这种支持包括了一个更新的 AccountController、便于注册和账户管理的视图以及构建在流行库 DotNetOpenAuth 之上的工具类!

           新的登录页面会出现两个登录的选项,如下图:

           image

    注册外部登录提供器

           需要显式的启用外部网站,以便利用它们的账户登录我们的网站。可喜的是,这个操作非常简单,可以在 AuthConfig.cs 中配置授权提供程序。默认文件中的所有验证提供器都会注释掉,如下:

    public static void RegisterAuth()
    {
        // 若要允许此站点的用户使用他们在其他站点(例如 Microsoft、Facebook 和 Twitter)上拥有的帐户登录,
        // 必须更新此站点。有关详细信息,请访问 http://go.microsoft.com/fwlink/?LinkID=252166
     
        //OAuthWebSecurity.RegisterMicrosoftClient(
        //    clientId: "",
        //    clientSecret: "");
     
        //OAuthWebSecurity.RegisterTwitterClient(
        //    consumerKey: "",
        //    consumerSecret: "");
     
        //OAuthWebSecurity.RegisterFacebookClient(
        //    appId: "",
        //    appSecret: "");
     
        //OAuthWebSecurity.RegisterGoogleClient();
    }

           使用OAuth提供器的网站(如Facebook、Twitter等)要求我们把网站注册为一个应用程序,这样它们就会提供我们一个客户端 id 和一个口令,我们利用 OAuth 提供器就可以进行验证。利用 OpenID(如 Google 和 Yahoo)的网站不需要注册应用程序,我们也不需要客户端 id 和口令。

    配置 OpenID 提供器

           由于不用注册,不用填写参数,因此配置 OpenID 提供器是非常简单的,Google、Yahoo 等都有现成的实现,而 myOpenID 需要创建注册一个自定义的客户端(通过 using 语句引入一些必要的命名空间,位于 DotNetOpenAuth 下):

    using Microsoft.Web.WebPages.OAuth;
    using DotNetOpenAuth.AspNet.Clients;
    using DotNetOpenAuth.OpenId.RelyingParty;
     
    namespace OAuthMVC
    {
        public static class AuthConfig
        {
            public static void RegisterAuth()
            {
                // 配置 Google 提供器
                OAuthWebSecurity.RegisterGoogleClient();
     
                // 配置 Yahoo 提供器
                OAuthWebSecurity.RegisterYahooClient();
     
                // 配置 myOpenID 提供器,先创建 OpenIdClient,再进行注册
                var MyOpenIdClient = new OpenIdClient("myopenid", WellKnownProviders.MyOpenId);
                OAuthWebSecurity.RegisterClient(MyOpenIdClient, "MyOpenID", null);
            }
        }
    }

           运行程序,测试登录的效果,如图:

    image

           Google 被和谐,点击 Yahoo 可以导航到登录界面:

           image

           输入账户密码之后,Yahoo 询问是否同意登录我们的网站,选择 Agree:

           image

           验证成功,并授权后,浏览器返回我们的站点,此时可以完成一些我们站点的注册步骤,并在单击注册后,会被作为一个已认定的用户重定向到主页:

           image

           注册成功:

           image

          点击用户名可以管理自己的账户,添加一个本地账户和密码或者绑定额外的外部登录提供器:

           image

    配置 OAuth 提供器

           需要在第三方网站将自己的站点注册为一个 App,之后使用获得的 AppID 和密钥就可以注册了,例如下面的 Facebook 站点:

    public static void RegisterAuth()
    {
        OAuthWebSecurity.RegisterFacebookClient(appId: "123456789", appSecret: "abcdefg");
    }

    外部登录的安全性

           尽管 OAuth 和 OpenID 简化了安全性编码,但也给应用程序引入了其他潜在的攻击媒介,如果一个提供器网站被破坏,或者网站之间的安全通信遭到破坏,攻击者可能会破坏我们的网站登录、或者捕获用户信息。因此要做到以下几点:

           1. 可信的外部登录提供器,使用知名的提供器,只支持我们信任的站点

           2. 要求 SSL 登录,外部提供器到网站的回调中包含拥有用户信息的安全令牌,当令牌在互联网传递时,使用 HTTPS 传输是很重要的,这样可以防止信息被拦截。

    [RequireHttps]
    [AllowAnonymous]
    public ActionResult Login(string returnUrl)
    {
        ViewBag.ReturnUrl = returnUrl;
        return View();
    }

     

  • 相关阅读:
    Android(java)学习笔记204:JNI之native方法头文件的生成
    Linux学习笔记01:Linux下的drwxr-xr-x
    Java基础知识强化49:10个实用的但偏执的Java编程技术
    大数据笔记12:Hadoop安装之安装JDK
    大数据笔记11:MapReduce的运行流程
    大数据笔记10:大数据之Hadoop的MapReduce的原理
    大数据笔记09:大数据之Hadoop的HDFS使用
    大数据笔记08:云计算(云)
    Java基础知识强化48:Java中哈希码
    hdu 1397 (素数判定)
  • 原文地址:https://www.cnblogs.com/SkySoot/p/4899482.html
Copyright © 2020-2023  润新知