.net架構|asp.net|建立
本來標題應當是,利用.NET架構創作安全性網站。
這是從MSDN上摘抄整理而來的,結合我自己的經驗之談。
我看了有很多朋友都在嘗試寫出帶有登陸這樣功能的網站,其方法幾乎都是驗證使用者的登陸合法,然後發送一個表示驗證的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。
後記
.NET提供了完整的安全方面的解決方案,相對於ASP,這是激動人心的一個新特性。只是很多人可能並不能夠熟練地運用,而且最痛心的是,很多書籍上甚至並沒有這方面的任何描述,甚至連概念都沒有。這就讓人很懷疑編者的水平了。
首先,還是要在不斷的實踐過程中去瞭解和體會.NET,其實最好的老師應當是MSDN,到論壇來發帖的使用者,我都盡量建議去查閱MSDN的資料,MSDN除了教給你怎麼寫代碼,其實他教給你的還有非常優秀的思想和整體概念。只要學會使用,沒有這些書也可以。從寫第一行代碼到現在,除了一本啟蒙書,其他的資源都是MSDN或者網上找的,還有就是在每次做項目中的心得。儘管現在看來,啟蒙書中也沒有非常全面地講述這些東西。