利用.NET框架創作安全性網站

有很多朋友都在嘗試寫出帶有登陸這樣功能的網站,其方法幾乎都是驗證用戶的登陸合法,然後發送一個表示驗證的Cookie,或者在Session中保存信息以便於追蹤接下來的訪問授權,其實,這些細節化的操作,.NET都提供了一種非常有效的解決辦法,能使你從繁瑣的安全驗證上解脫出來,而且,儘管你可能很小心地定義那些頁面不能被沒有權限的人訪問,然而還有可能出現一些無法被檢查出來的漏洞讓他們跳過安全驗證


好,廢話少說,本文將介紹如下內容:
1、關於登陸驗證和授權
2、使用Forms驗證模式
3、授權資源的訪問
4、基於角色的授權


1、關於登陸驗證和授權
    很多網站都有登陸對話框,讓事先已經註冊的用戶驗證,以便爲他們提供個性化的服務等。可以把這個過程看作是兩件事情的發生:驗證和授權!登陸的作用是驗證請求登陸的用戶是否合法,而授權則是驗證合法的用戶在請求資源時,根據他們的權限決定是訪問還是拒絕。
    以上這種網站本身提供對話框的作法在.NET中被稱之爲Forms驗證模式,接下來將會講述這種驗證模式。在以前ASP陳序員或者其他程序員,要想保存合法用戶的驗證,在以後的訪問授權中使用,不得不使用寫Cookie或者將信息保存在Session中的方法,而在需要授權的頁面加載前添加一堆繁瑣的代碼來驗證制定的用戶是否具有訪問權限否則的話就不能顯示頁面的內容,最惱火的是在授權頁面上添加這些代碼讓人覺得重複和繁瑣,而且可能不是最安全的,有一些比較隱蔽的方式可能會輕易繞過這種驗證,因此程序員將來要做的很多事情就是再修改代碼已堵住在運行過程中才發現的漏洞。在.NET的System.Web.Security中提供了一些網站安全方面的解決方案,儘管驗證用戶合法和授權的基本思路沒有變化,但是授權的工作幾乎已經交給.NET框架了,我們些代碼之需要自己驗證用戶合法,並且告訴框架這個用戶合法即可。

2、使用Forms驗證模式
    要使用啓用Forms驗證模式,請在網站根目錄下的web.config文件中添加如下配置:(注意區分大小寫)

<configuration>
  <system.web>
    <authentication mode="Forms" />
  </system.web>
</configuration>


    這將告訴.NET,你的網站使用Forms驗證模式,.NET將不參與驗證用戶的工作,而是將這個工作交給你完成,你必須自己編寫一些代碼來驗證用戶合法,並且報告給.NET用戶是合法的。.NET將會發送一個驗證Cookie到用戶,隨後的訪問中,.NET以此Cookie爲依據,來執行授權的操作。

    例如我們在login.aspx界面中放置兩個接受輸入的文本框txtUserName和txtPassword,在數據庫中,保存了用戶名UserName和密碼UserPassword,使用btnLogin按鈕的Click事件來驗證用戶:

private void btnLogin_Click(object sender, EventArgs e)
{
  string sql = "SELECT userid FROM Users WHERE UserName = '" + txtUserName.Text.Replace("'","_") + "' AND UserPassword = '" + System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(txtPassword.Text, "md5") + "'";
  //使用上面類似的SQL語句向數據庫執行查詢,如果用戶是合法的,將會返回數據。
  if (...) //根據條件判定用戶是合法的
  {
    //下面的語句告訴.NET發送一個驗證Cookie給用戶:
    System.Web.Security.FormsAuthentication.SetAuthCookie(userid, false)
    Response.Redirect("afterlogin.aspx");  //定位到登陸後頁面
  }
  else
  {
     //用戶不合法,提示錯誤信息
  }
}

以上代碼中,
txtUserName.Text.Replace("'","_")將用戶輸入的文本中單引號替換爲下劃線,以防止SQL注入攻擊。
System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(txtPassword.Text, "md5")方法將txtPassword.Text轉換爲MD5散列值,注意,在用戶註冊的時候,同樣使用此方法將其輸入的註冊密碼轉換爲散列值存儲在數據庫中,這裏將用戶輸入的散列值進行對比以決定是否合法用戶。任何時候不要將敏感的文本信息以明文方式存放在數據庫中。通過MD5加密,即便此密文被截獲,攻擊者仍無法獲得真實的密碼。

當確認用戶驗證是合法的,則調用System.Web.Security.FormsAuthentication.SetAuthCookie(userid, false)方法,發送驗證Cookie,此方法傳遞兩個參數,一個是代表用戶的標示,一般來說,在接下來確認用戶唯一身份的就是從數據庫中獲得的userid。第二個參數告訴.NET是否寫入持續的Cookie,如果爲true,則Cookie將被持續,下次用戶再次訪問時,Cookie仍存在(相當於記住用戶,可以提供這樣的複選框讓用戶來決定是否持續Cookie)。發送了Cookie後,即可調用跳轉語句跳轉到指定地方。

另外還有一個方法:Web.Security.FormsAuthentication.RedirectFromLoginPage(string UserName, bool);將發送Cookie,並且根據傳遞的ReturnUrl參數來跳轉到指定的頁面(相當於將上面的兩個步驟合爲一步)。因此login.aspx隱含可以傳遞ReturnUrl,如果沒有這個參數,這個方法將用戶跳轉到Default.aspx頁。

3、授權資源的訪問
    一旦驗證了用戶合法,接下來要做的事就是對於用戶請求的資源,授權他們是否能夠訪問。重新回到web.config文件中,在網站的任何目錄中都可以使用web.config,他們的設置是傳遞繼承的。
    例如在users目錄中存放的均是當用戶登錄後才能訪問的頁面,則在這個目錄中創建一個web.config文件,內容如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <!--  授權
           此節設置應用程序的授權策略。可以允許或拒絕不同的用戶或角色訪問
          應用程序資源。通配符: "*" 表示任何人,"?" 表示匿名
          (未經身份驗證的)用戶。
    -->
    <authorization>
        <deny users="?" />
    </authorization>
  </system.web>
</configuration>

上述內容中deny users="?" 將告訴.NET,此目錄拒絕匿名用戶的訪問,也就是沒有驗證的用戶。當用戶試圖請求此目錄中的資源,將會被重新定向到login.aspx頁面,要求登陸。沒有登陸的情況下是無法訪問的。

上述僅對目錄進行定義,程序員不用在頁面上添加任何代碼,即可完整地實現了授權方案。
當然,這種僅針對目錄的授權配置可能有時候又會缺乏靈活,因此,.NET也提供location配置節,可以對指定的資源定義授權:
<configuration>
  <location path="userabc.aspx">
    <system.web>
      <authorization>
        <allow users="a,b,c" />
      </authorization>
    </system.web>
  </location>
</configuration>

其中path是資源相對路徑。

如果這還不夠靈活的話,.NET也提供了在代碼中使用的方法,ASP.NET頁全局隱含了一個只讀的User對象,通過獲取User.Identity.IsAuthenticated屬性,可探知用戶是否驗證(即是否登陸),User.Identity.Name屬性可以獲得用戶的Name,即在驗證時的SetAuthCookie方法中傳遞的userid。

4、基於角色的授權
    上面我們講述的用戶驗證,只可能有兩種情況,要麼用戶通過驗證,可以授權訪問資源,要麼用戶沒有通過驗證,不能訪問需要授權的資源。但是即便是驗證通過的用戶,可能他們所持用的權限還需要再進一步區分。例如普通用戶和管理員同樣是需要驗證通過的,但是普通用戶顯然不能夠訪問管理頁面,而管理員可以。面對這種情況,.NET可以使用基於角色的授權模型。
    其基本原理是,一旦用戶驗證合法,他們就被分配角色,用戶可以使一個或者若干和角色,而資源的授權面向角色,這樣,針對不同的角色,就可以授予不同的權限,沒有某種角色類型的用戶試圖訪問需要這種角色的資源將會被拒絕。
    當網站開始接受用戶請求時,就伴隨着驗證,將激發Application_AuthenticateRequest事件,在Global.asax文件中寫代碼以響應此事件。角色的分配工作就需要再這裏進行。

public void Application_AuthenticateRequest(object sender, EventArgs e)
{
      if (this.Request.IsAuthenticated)
      {
            //這裏簡化了操作,可以從數據庫中獲得角色信息用以構造rolesStrArr數組。作爲示例,我們爲除了a之外的用戶分配了管理員角色
            string[] rolesStrArr;
            if (this.Context.User.Identity.Name == "a")
            {
                  rolesStrArr = new string[]{"普通用戶"};
            }
            else
            {
                  rolesStrArr = new string[]{"普通用戶","管理員"};
            }
            this.Context.User = new System.Security.Principal.GenericPrincipal(this.User.Identity, rolesStrArr);
      }
}

以上代碼清晰明瞭,因此不再贅述。雖然在全局性有User對象,但是隻有Context上下文中的User對象是可以寫入的,我們調用System.Security.Principal.GenericPrincipal方法,在原有User對象的基礎上爲其加入角色。角色列表示一個字符串數組。

    一旦用戶被授予訪問角色之後,在web.config中就可以配置針對不同角色的訪問。例如在管理員admin目錄內

<configuration>
  <location path="userabc.aspx">
    <system.web>
      <authorization>
        <allow roles="管理員" />
        <deny users="*" />
      </authorization>
    </system.web>
  </location>
</configuration>

上述配置只允許管理員角色才能被授權。資源默認是任何人都訪問的,所以要在下面再添加<deny users="*" />表示對任何用戶拒絕。

注意,無論對角色或者對用戶指定資源的訪問,如果對於多個角色或者讀個資源,他們之間使用半角逗號隔開。同樣,也可以使用上面講到的方法,對指定的資源進行配置而不是對整個目錄。

全局的User對象提供了一個方法IsInRole(string RoleName)方法用來在代碼中檢測用戶是否擁有某種角色。如果他擁有這種角色,將返回true。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章