zoukankan      html  css  js  c++  java
  • 成员资格、授权 – 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();
    }

     

  • 相关阅读:
    阿里云 k8s 部署 Spring Cloud Alibaba 微服务实践 (四) 自动化部署
    阿里云 k8s 部署 Spring Cloud Alibaba 微服务实践 (三) 服务观测
    阿里云 k8s 部署 Spring Cloud Alibaba 微服务实践 (二) 部署微服务程序
    阿里云 k8s 部署 Spring Cloud Alibaba 微服务实践 (一) 部署 Nacos
    C++知识点
    libmkl 学习笔记
    基于tesseract-OCR进行中文识别
    poco编译与运行
    Linux下的I/O复用与epoll详解(转载)
    高并发网络编程之epoll详解(转载)
  • 原文地址:https://www.cnblogs.com/SkySoot/p/4899482.html
Copyright © 2011-2022 走看看