詳解ASP.NET中Membership表和阻止DOS攻擊
無(wú)需關(guān)閉站點(diǎn)的情況下如何查詢ASP.NET 2.0 Membership表
這種查詢會(huì)爽快的運(yùn)行在你的開(kāi)發(fā)環(huán)境中:
|
或者在無(wú)需任何問(wèn)題的情況下獲得一些用戶的 profile :
|
甚至你可以像這樣在aspnet_membership表中輕松的更新用戶的郵件地址:
|
但是當(dāng)在你的產(chǎn)品服務(wù)器上有一個(gè)巨大的數(shù)據(jù)庫(kù)時(shí),運(yùn)行這些腳本都會(huì)搞垮你的服務(wù)器。理由就是,雖然這些查詢看起來(lái)明顯是你經(jīng)常使用的語(yǔ)句,但是這些語(yǔ)句沒(méi)有任何索引。因此,上面所有在"Table Scan"中的結(jié)果(很糟糕的查詢方式)都會(huì)響應(yīng)數(shù)百萬(wàn)行記錄。
這里的現(xiàn)象對(duì)我們而言意味著什么。我們使用了這些字段諸如UserName, Email, UserID, IsAnonymous等。關(guān)于Pageflakes的很多市場(chǎng)報(bào)表。這些報(bào)表中的一些僅僅市場(chǎng)部可以使用,其他人都不可以使用它?,F(xiàn)在,站點(diǎn)運(yùn)行得很好,但是市場(chǎng)部一天當(dāng)中會(huì)使用它很多次而且用戶常常打電話告訴我們網(wǎng)站太慢了,”用戶報(bào)告網(wǎng)站性能十分地慢!”,一些頁(yè)面甚至還發(fā)生超時(shí)等現(xiàn)象。
常常,當(dāng)他們打電話個(gè)給我們的時(shí)候,我們就會(huì)告訴他們“稍等,馬上檢查”,然后我們開(kāi)始對(duì)整個(gè)網(wǎng)站進(jìn)行檢查。我們使用SQL profiler工具來(lái)查看到底那里出錯(cuò)了。但是我們不能找到任何問(wèn)題。Profiler顯示了查詢運(yùn)行的摁件。CPU負(fù)載位于適當(dāng)?shù)膮?shù)范圍內(nèi)。站點(diǎn)運(yùn)行得很好,很流暢。我們打電話給他們,“我們不能查到任何問(wèn)題,到底怎么了?”。
因此,當(dāng)我們?cè)噲D調(diào)查這個(gè)問(wèn)題并且在我們沒(méi)有調(diào)查時(shí),發(fā)現(xiàn)站點(diǎn)真的變得很慢的時(shí)候,為什么我們不能看到它變慢的任何問(wèn)題?
市場(chǎng)部有時(shí)候每天需要很多次運(yùn)行一些分析報(bào)表來(lái)查詢數(shù)據(jù)。無(wú)論什么時(shí)候他們執(zhí)行這些查詢,當(dāng)這些字段都是不是索引時(shí),它會(huì)使得服務(wù)器的IO吞吐量更高以及CPU的使用率增高:
我們使用了15000 RPM的SCSI驅(qū)動(dòng)器,雖然很貴,但是運(yùn)行速度很快。CPU是雙核Dual Xeon 64位的。盡管有這些強(qiáng)大的硬件支持,但是由于這個(gè)巨大的數(shù)據(jù)庫(kù)在執(zhí)行查詢時(shí)仍然把我們壓倒了。
但是當(dāng)市場(chǎng)部給我們打電話時(shí),這些問(wèn)題從來(lái)沒(méi)發(fā)生過(guò),并且我們沒(méi)有掛斷電話并試圖找出問(wèn)題所在。因?yàn)楫?dāng)他們打電話給我們時(shí),會(huì)跟我們交談,他們沒(méi)有運(yùn)行使服務(wù)器垮掉的任何報(bào)表。讓他們停留在了站點(diǎn)上的其它地方,幾乎做了和用戶一樣的事情.
咱們看看如下索引:
表: aspnet_users
◆簇索引 = ApplicationID, LoweredUserName
◆非簇索引 = ApplicationID, LastActivityDate
◆主鍵 = UserID
表: aspnet_membership
◆簇索引 = ApplicationID, LoweredEmail
◆非簇索引= UserID
表: aspnet_Profile
◆簇索引 = UserID
大部分索引都有ApplicationID在其中。除非你放置ApplicationID='…'在WHERE從句中,此時(shí)它就不會(huì)使用任何索引。結(jié)果,所有的查詢會(huì)進(jìn)行全表掃描。僅僅是把ApplicationID放在where從句中(從aspnet_Application表中找到你的ApplicationID)所有的查詢就會(huì)變得非???。
不要在WHERE從句中使用Email或者UserName。他們不是索引的一部分,從而取代LoweredUserName和LoweredEmail字段。所有的查詢必須在WHERE從句中有ApplicationID。
我們的管理站點(diǎn)上包涵了很多這種報(bào)表并且每個(gè)報(bào)表都包涵了很多這種在表aspnet_users、 aspnet_membership 和aspnet_Profile上的查詢。結(jié)果,當(dāng)市場(chǎng)部試圖產(chǎn)生報(bào)表時(shí),它們會(huì)占用所有CPU、HDD的資源,最終導(dǎo)致站點(diǎn)變得非常慢甚至毫無(wú)響應(yīng)。
確保你總是檢查了所有的WHERE和JOIN從句。否則當(dāng)你的網(wǎng)站上線時(shí)會(huì)出現(xiàn)大問(wèn)題。
阻止決絕服務(wù)(DOS)攻擊
對(duì)黑客而言,Web服務(wù)是最容被襲擊的目標(biāo),因?yàn)樯踔烈粋€(gè)入門級(jí)的黑客都可以通過(guò)重復(fù)調(diào)用一個(gè)代價(jià)昂貴的Web服務(wù)來(lái)使服務(wù)器死掉。像Pageflakes這樣以Ajax技術(shù)構(gòu)建的頁(yè)面并將其作為起始頁(yè)的站點(diǎn)將是DOS攻擊的最大目標(biāo),因?yàn)槿绻銉H僅是在無(wú)需保存cookie的情況下重復(fù)地訪問(wèn)主頁(yè),這樣每次點(diǎn)擊都會(huì)產(chǎn)生一個(gè)新的用戶、新的頁(yè)面配置、新的widgets等。第一次訪問(wèn)是花費(fèi)代價(jià)最昂貴的一次。
可是這也是最容易暴露并搞垮站點(diǎn)的一次機(jī)會(huì)。你可以自己試試這種方式,僅僅需要如下一段簡(jiǎn)單的代碼即可:
|
令你感到吃驚的是,你會(huì)注意到,在進(jìn)行兩次調(diào)用之后,你不會(huì)得到一個(gè)有效的響應(yīng)。這并不是說(shuō)你已經(jīng)成功地將服務(wù)器搞垮了。而是因?yàn)槟愕恼?qǐng)求被決絕了。你可能會(huì)樂(lè)意你不再獲得任何服務(wù)了,因此你達(dá)到了決絕服務(wù)(你自己)的目的。我們很高興決絕了你的服務(wù)(DYOS)。
我提出的這種簡(jiǎn)便技巧就是記住從某個(gè)特殊IP地址發(fā)來(lái)的請(qǐng)求。當(dāng)請(qǐng)求的數(shù)量超過(guò)閥門值時(shí),便決絕后續(xù)發(fā)來(lái)的請(qǐng)求。這個(gè)辦法是在ASP.NET緩存中記住調(diào)用者的IP并為每個(gè)IP維護(hù)一個(gè)計(jì)數(shù)的請(qǐng)求。當(dāng)該計(jì)數(shù)值超過(guò)了預(yù)定義的限定值時(shí),決絕一段時(shí)期后的發(fā)來(lái)的請(qǐng)求,比如10分鐘。10分鐘后,再次允許來(lái)自那個(gè)IP的請(qǐng)求。
我有一個(gè)名為ActionValidator的類,該類包涵了一些具體的活動(dòng)諸如首次訪問(wèn)、再次訪問(wèn)、異步回傳、添加新的widget、添加新的頁(yè)面等等。檢查這種指定活動(dòng)的計(jì)數(shù)值是否指向了一個(gè)IP閥門值。
|
該枚舉包涵了活動(dòng)的類型以便用來(lái)在某個(gè)持續(xù)的時(shí)間――10分鐘檢查它們的閥門值。
使用 一個(gè)名為IsValid的static方法用來(lái)進(jìn)行檢查。如果請(qǐng)求限制沒(méi)有通過(guò),則返回true。如果請(qǐng)求需要被決絕,則返回false。一旦你獲得false,你就可以調(diào)用Request.End ()方法并阻止ASP.NET進(jìn)行未來(lái)的處理。你可以切換顯示了“Congratulations! You have succeeded in Denial of Service Attack.”的頁(yè)面。
|
內(nèi)置的緩存鍵將活動(dòng)類型和客戶端IP地址進(jìn)行了合并。首先它檢查是否有針對(duì)活動(dòng)的任何入口和存儲(chǔ)于緩存中的客戶端IP。如果沒(méi)有,開(kāi)始計(jì)數(shù)并記住指定持續(xù)時(shí)間內(nèi)緩存中的IP。緩存項(xiàng)中的絕對(duì)期滿日期確保緩存項(xiàng)在持續(xù)一段時(shí)間后能夠被清除并重新計(jì)算。當(dāng)在緩存中已經(jīng)存在一個(gè)入口時(shí),獲得最后的點(diǎn)擊數(shù),并檢查是否超越了該限制。如果沒(méi)有超過(guò),則增加計(jì)數(shù)器。
不需要通過(guò)Cache[url]=hit的方式存儲(chǔ)更新后的值到緩存中;因?yàn)辄c(diǎn)擊對(duì)象是一個(gè)引用對(duì)象,改變它意味著在緩存中它也要改變。事實(shí)上,如果你再次將它放入緩存中,緩存期滿計(jì)數(shù)器將重新開(kāi)始并在指定持續(xù)時(shí)期后導(dǎo)致失敗邏輯重新計(jì)算。
用法十分簡(jiǎn)單, default.Aspx頁(yè)面上的代碼如下:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);// Check if revisit is valid or not
if( !base.IsPostBack )
{
// Block cookie less visit attempts
if( Profile.IsFirstVisit )
{
if( !ActionValidator.IsValid(ActionValidator.ActionTypeEnum.FirstVisit))
Response.End();
}
else
{
if( !ActionValidator.IsValid(ActionValidator.ActionTypeEnum.ReVisit) )
Response.End();
}
}
else
{
// Limit number of postbacks
if( !ActionValidator.IsValid(ActionValidator.ActionTypeEnum.Postback) )
Response.End();
}
}
這里我對(duì)具體的場(chǎng)景如訪問(wèn)、再次訪問(wèn)、回發(fā)等進(jìn)行檢查。
當(dāng)然你可以通過(guò)放置一些Cisco的防火墻來(lái)阻止DOS攻擊。這樣你就可以避免主機(jī)提供商的整個(gè)網(wǎng)絡(luò)不會(huì)因?yàn)槭艿紻OS或者DDOS攻擊的影響。這些所能保證的只是網(wǎng)絡(luò)級(jí)別的攻擊,諸如TCP SYN攻擊、奇怪的數(shù)據(jù)報(bào)文分組等。在沒(méi)有cookie或者試圖加載太多widgets的情況下, 他們沒(méi)有辦法能夠分析這些報(bào)文并找出一個(gè)特殊的IP來(lái)試圖多次加載站點(diǎn)。這些都被稱為應(yīng)用程序級(jí)別的DOS攻擊,硬件不能對(duì)其進(jìn)行阻止。這些必須在你自己的代碼中進(jìn)行實(shí)現(xiàn)處理。
現(xiàn)在幾乎很少有站點(diǎn)采用這種預(yù)防措施來(lái)防止遭受這種應(yīng)用程序級(jí)別的DOS攻擊。因此可以非常容易地編寫一些簡(jiǎn)單的代碼從寬帶連接中通過(guò)持續(xù)點(diǎn)擊站點(diǎn)上這些耗費(fèi)昂貴的頁(yè)面或者進(jìn)行Web服務(wù)調(diào)用來(lái)使得服務(wù)器崩潰。我希望這對(duì)你來(lái)說(shuō)只是一個(gè)小問(wèn)題,并可以采用這種有效的方式幫助你阻止自己Web應(yīng)用程序遭受DOS攻擊。
小結(jié)
你已經(jīng)學(xué)到了很多技巧來(lái)應(yīng)付在具有相同硬件配置的機(jī)器上使得ASP.NET的性能發(fā)揮到極致。你也學(xué)到了如何使用一些AJAX技術(shù)來(lái)使得你的站點(diǎn)加載更快更流暢。最后,你學(xué)到了如何防御站點(diǎn)因高點(diǎn)擊量所帶來(lái)的風(fēng)險(xiǎn)以及擴(kuò)展靜態(tài)內(nèi)容跨過(guò)內(nèi)容傳輸網(wǎng)絡(luò)來(lái)抵御站點(diǎn)高峰期引起的阻塞。所有這些技術(shù)都可以使你的站點(diǎn)加載速度更快,運(yùn)行得更流暢,以及以更低的成本獲得更高的負(fù)載量。
【編輯推薦】