進(jìn)入本貼討論:http://bbs./thread-67635-1-1.html
本來(lái)標(biāo)題應(yīng)當(dāng)是,利用.NET框架創(chuàng)作安全性網(wǎng)站。
這是從MSDN上摘抄整理而來(lái)的,結(jié)合我自己的經(jīng)驗(yàn)之談。
我看
了有很多朋友都在嘗試寫(xiě)出帶有登陸這樣功能的網(wǎng)站,其方法幾乎都是驗(yàn)證用戶的登陸合法,然后發(fā)送一個(gè)表示驗(yàn)證的Cookie,或者在Session中保存
信息以便于追蹤接下來(lái)的訪問(wèn)授權(quán),其實(shí),這些細(xì)節(jié)化的操作,.NET都提供了一種非常有效的解決辦法,能使你從繁瑣的安全驗(yàn)證上解脫出來(lái),而且,盡管你可
能很小心地定義那些頁(yè)面不能被沒(méi)有權(quán)限的人訪問(wèn),然而還有可能出現(xiàn)一些無(wú)法被檢查出來(lái)的漏洞讓他們跳過(guò)安全驗(yàn)證
好,廢話少說(shuō),本文將介紹如下內(nèi)容:
1、關(guān)于登陸驗(yàn)證和授權(quán)
2、使用Forms驗(yàn)證模式
3、授權(quán)資源的訪問(wèn)
4、基于角色的授權(quán)
1、關(guān)于登陸驗(yàn)證和授權(quán)
很多網(wǎng)站都有登陸對(duì)話框,讓事先已經(jīng)注冊(cè)的用戶驗(yàn)證,以便為他們提供個(gè)性化的服務(wù)等??梢园堰@個(gè)過(guò)程看作是兩件事情的發(fā)生:驗(yàn)證和授權(quán)!登陸的作用是驗(yàn)證請(qǐng)求登陸的用戶是否合法,而授權(quán)則是驗(yàn)證合法的用戶在請(qǐng)求資源時(shí),根據(jù)他們的權(quán)限決定是訪問(wèn)還是拒絕。
以
上這種網(wǎng)站本身提供對(duì)話框的作法在.NET中被稱(chēng)之為Forms驗(yàn)證模式,接下來(lái)將會(huì)講述這種驗(yàn)證模式。在以前ASP陳序員或者其他程序員,要想保存合法
用戶的驗(yàn)證,在以后的訪問(wèn)授權(quán)中使用,不得不使用寫(xiě)Cookie或者將信息保存在Session中的方法,而在需要授權(quán)的頁(yè)面加載前添加一堆繁瑣的代碼來(lái)
驗(yàn)證制定的用戶是否具有訪問(wèn)權(quán)限否則的話就不能顯示頁(yè)面的內(nèi)容,最?lèi)阑鸬氖窃谑跈?quán)頁(yè)面上添加這些代碼讓人覺(jué)得重復(fù)和繁瑣,而且可能不是最安全的,有一些比
較隱蔽的方式可能會(huì)輕易繞過(guò)這種驗(yàn)證,因此程序員將來(lái)要做的很多事情就是再修改代碼已堵住在運(yùn)行過(guò)程中才發(fā)現(xiàn)的漏洞。在.NET的
System.Web.Security中提供了一些網(wǎng)站安全方面的解決方案,盡管驗(yàn)證用戶合法和授權(quán)的基本思路沒(méi)有變化,但是授權(quán)的工作幾乎已經(jīng)交
給.NET框架了,我們些代碼之需要自己驗(yàn)證用戶合法,并且告訴框架這個(gè)用戶合法即可。
2、使用Forms驗(yàn)證模式
要使用啟用Forms驗(yàn)證模式,請(qǐng)?jiān)诰W(wǎng)站根目錄下的web.config文件中添加如下配置:(注意區(qū)分大小寫(xiě))
<configuration>
<system.web>
<authentication mode="Forms" />
</system.web>
</configuration>
這
將告訴.NET,你的網(wǎng)站使用Forms驗(yàn)證模式,.NET將不參與驗(yàn)證用戶的工作,而是將這個(gè)工作交給你完成,你必須自己編寫(xiě)一些代碼來(lái)驗(yàn)證用戶合法,
并且報(bào)告給.NET用戶是合法的。.NET將會(huì)發(fā)送一個(gè)驗(yàn)證Cookie到用戶,隨后的訪問(wèn)中,.NET以此Cookie為依據(jù),來(lái)執(zhí)行授權(quán)的操作。
例如我們?cè)趌ogin.aspx界面中放置兩個(gè)接受輸入的文本框txtUserName和txtPassword,在數(shù)據(jù)庫(kù)中,保存了用戶名UserName和密碼UserPassword,使用btnLogin按鈕的Click事件來(lái)驗(yàn)證用戶:
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") + "'";
//使用上面類(lèi)似的SQL語(yǔ)句向數(shù)據(jù)庫(kù)執(zhí)行查詢,如果用戶是合法的,將會(huì)返回?cái)?shù)據(jù)。
if (...) //根據(jù)條件判定用戶是合法的
{
//下面的語(yǔ)句告訴.NET發(fā)送一個(gè)驗(yàn)證Cookie給用戶:
System.Web.Security.FormsAuthentication.SetAuthCookie(userid, false)
Response.Redirect("afterlogin.aspx"); //定位到登陸后頁(yè)面
}
else
{
//用戶不合法,提示錯(cuò)誤信息
}
}
以上代碼中,
txtUserName.Text.Replace("'","_")將用戶輸入的文本中單引號(hào)替換為下劃線,以防止SQL注入攻擊。
System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(txtPassword.Text,
"md5")方法將txtPassword.Text轉(zhuǎn)換為MD5散列值,注意,在用戶注冊(cè)的時(shí)候,同樣使用此方法將其輸入的注冊(cè)密碼轉(zhuǎn)換為散列值存儲(chǔ)在
數(shù)據(jù)庫(kù)中,這里將用戶輸入的散列值進(jìn)行對(duì)比以決定是否合法用戶。任何時(shí)候不要將敏感的文本信息以明文方式存放在數(shù)據(jù)庫(kù)中。通過(guò)MD5加密,即便此密文被截
獲,攻擊者仍無(wú)法獲得真實(shí)的密碼。
當(dāng)確認(rèn)用戶驗(yàn)證是合法的,則調(diào)用
System.Web.Security.FormsAuthentication.SetAuthCookie(userid,
false)方法,發(fā)送驗(yàn)證Cookie,此方法傳遞兩個(gè)參數(shù),一個(gè)是代表用戶的標(biāo)示,一般來(lái)說(shuō),在接下來(lái)確認(rèn)用戶唯一身份的就是從數(shù)據(jù)庫(kù)中獲得的
userid。第二個(gè)參數(shù)告訴.NET是否寫(xiě)入持續(xù)的Cookie,如果為true,則Cookie將被持續(xù),下次用戶再次訪問(wèn)時(shí),Cookie仍存在
(相當(dāng)于記住用戶,可以提供這樣的復(fù)選框讓用戶來(lái)決定是否持續(xù)Cookie)。發(fā)送了Cookie后,即可調(diào)用跳轉(zhuǎn)語(yǔ)句跳轉(zhuǎn)到指定地方。
另
外還有一個(gè)方法:Web.Security.FormsAuthentication.RedirectFromLoginPage(string
UserName,
bool);將發(fā)送Cookie,并且根據(jù)傳遞的ReturnUrl參數(shù)來(lái)跳轉(zhuǎn)到指定的頁(yè)面(相當(dāng)于將上面的兩個(gè)步驟合為一步)。因此
login.aspx隱含可以傳遞ReturnUrl,如果沒(méi)有這個(gè)參數(shù),這個(gè)方法將用戶跳轉(zhuǎn)到Default.aspx頁(yè)。
3、授權(quán)資源的訪問(wèn)
一旦驗(yàn)證了用戶合法,接下來(lái)要做的事就是對(duì)于用戶請(qǐng)求的資源,授權(quán)他們是否能夠訪問(wèn)。重新回到web.config文件中,在網(wǎng)站的任何目錄中都可以使用web.config,他們的設(shè)置是傳遞繼承的。
例如在users目錄中存放的均是當(dāng)用戶登錄后才能訪問(wèn)的頁(yè)面,則在這個(gè)目錄中創(chuàng)建一個(gè)web.config文件,內(nèi)容如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<!-- 授權(quán)
此節(jié)設(shè)置應(yīng)用程序的授權(quán)策略??梢栽试S或拒絕不同的用戶或角色訪問(wèn)
應(yīng)用程序資源。通配符: "*" 表示任何人,"?" 表示匿名
(未經(jīng)身份驗(yàn)證的)用戶。
-->
<authorization>
<deny users="?" />
</authorization>
</system.web>
</configuration>
上述內(nèi)容中deny users="?" 將告訴.NET,此目錄拒絕匿名用戶的訪問(wèn),也就是沒(méi)有驗(yàn)證的用戶。當(dāng)用戶試圖請(qǐng)求此目錄中的資源,將會(huì)被重新定向到login.aspx頁(yè)面,要求登陸。沒(méi)有登陸的情況下是無(wú)法訪問(wèn)的。
上述僅對(duì)目錄進(jìn)行定義,程序員不用在頁(yè)面上添加任何代碼,即可完整地實(shí)現(xiàn)了授權(quán)方案。
當(dāng)然,這種僅針對(duì)目錄的授權(quán)配置可能有時(shí)候又會(huì)缺乏靈活,因此,.NET也提供location配置節(jié),可以對(duì)指定的資源定義授權(quán):
<configuration>
<location path="userabc.aspx">
<system.web>
<authorization>
<allow users="a,b,c" />
</authorization>
</system.web>
</location>
</configuration>
其中path是資源相對(duì)路徑。
如
果這還不夠靈活的話,.NET也提供了在代碼中使用的方法,ASP.NET頁(yè)全局隱含了一個(gè)只讀的User對(duì)象,通過(guò)獲取
User.Identity.IsAuthenticated屬性,可探知用戶是否驗(yàn)證(即是否登陸),User.Identity.Name屬性可以獲
得用戶的Name,即在驗(yàn)證時(shí)的SetAuthCookie方法中傳遞的userid。
4、基于角色的授權(quán)
上面我們講述
的用戶驗(yàn)證,只可能有兩種情況,要么用戶通過(guò)驗(yàn)證,可以授權(quán)訪問(wèn)資源,要么用戶沒(méi)有通過(guò)驗(yàn)證,不能訪問(wèn)需要授權(quán)的資源。但是即便是驗(yàn)證通過(guò)的用戶,可能他
們所持用的權(quán)限還需要再進(jìn)一步區(qū)分。例如普通用戶和管理員同樣是需要驗(yàn)證通過(guò)的,但是普通用戶顯然不能夠訪問(wèn)管理頁(yè)面,而管理員可以。面對(duì)這種情
況,.NET可以使用基于角色的授權(quán)模型。
其基本原理是,一旦用戶驗(yàn)證合法,他們就被分配角色,用戶可以使一個(gè)或者若干和角色,而資源的授權(quán)面向角色,這樣,針對(duì)不同的角色,就可以授予不同的權(quán)限,沒(méi)有某種角色類(lèi)型的用戶試圖訪問(wèn)需要這種角色的資源將會(huì)被拒絕。
當(dāng)網(wǎng)站開(kāi)始接受用戶請(qǐng)求時(shí),就伴隨著驗(yàn)證,將激發(fā)Application_AuthenticateRequest事件,在Global.asax文件中寫(xiě)代碼以響應(yīng)此事件。角色的分配工作就需要再這里進(jìn)行。
public void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (this.Request.IsAuthenticated)
{
//這里簡(jiǎn)化了操作,可以從數(shù)據(jù)庫(kù)中獲得角色信息用以構(gòu)造rolesStrArr數(shù)組。作為示例,我們?yōu)槌薬之外的用戶分配了管理員角色
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對(duì)象,但是只有Context上下文中的User對(duì)象是可以寫(xiě)入的,我們調(diào)用
System.Security.Principal.GenericPrincipal方法,在原有User對(duì)象的基礎(chǔ)上為其加入角色。角色列表示一個(gè)
字符串?dāng)?shù)組。
一旦用戶被授予訪問(wèn)角色之后,在web.config中就可以配置針對(duì)不同角色的訪問(wèn)。例如在管理員admin目錄內(nèi)
<configuration>
<location path="userabc.aspx">
<system.web>
<authorization>
<allow roles="管理員" />
<deny users="*" />
</authorization>
</system.web>
</location>
</configuration>
上述配置只允許管理員角色才能被授權(quán)。資源默認(rèn)是任何人都訪問(wèn)的,所以要在下面再添加<deny users="*" />表示對(duì)任何用戶拒絕。
注意,無(wú)論對(duì)角色或者對(duì)用戶指定資源的訪問(wèn),如果對(duì)于多個(gè)角色或者讀個(gè)資源,他們之間使用半角逗號(hào)隔開(kāi)。同樣,也可以使用上面講到的方法,對(duì)指定的資源進(jìn)行配置而不是對(duì)整個(gè)目錄。
全局的User對(duì)象提供了一個(gè)方法IsInRole(string RoleName)方法用來(lái)在代碼中檢測(cè)用戶是否擁有某種角色。如果他擁有這種角色,將返回true。
后記
.NET提供了完整的安全方面的解決方案,相對(duì)于ASP,這是激動(dòng)人心的一個(gè)新特性。只是很多人可能并不能夠熟練地運(yùn)用,而且最痛心的是,很多書(shū)籍上甚至并沒(méi)有這方面的任何描述,甚至連概念都沒(méi)有。這就讓人很懷疑編者的水平了。
首
先,還是要在不斷的實(shí)踐過(guò)程中去了解和體會(huì).NET,其實(shí)最好的老師應(yīng)當(dāng)是MSDN,到論壇來(lái)發(fā)帖的用戶,我都盡量建議去查閱MSDN的資料,MSDN除
了教給你怎么寫(xiě)代碼,其實(shí)他教給你的還有非常優(yōu)秀的思想和整體概念。只要學(xué)會(huì)使用,沒(méi)有這些書(shū)也可以。從寫(xiě)第一行代碼到現(xiàn)在,除了一本啟蒙書(shū),其他的資源
都是MSDN或者網(wǎng)上找的,還有就是在每次做項(xiàng)目中的心得。盡管現(xiàn)在看來(lái),啟蒙書(shū)中也沒(méi)有非常全面地講述這些東西。
好了,希望大家看到會(huì)有所收獲。限于我的水平,錯(cuò)誤難免,歡迎指正!