ASP.NET 開發人員應當始終堅持的做法
沒有任何開發平臺,— 包括 ASP.NET在內 — 能夠保證一旦采用了該平臺,您就能夠編寫百分百安全的代碼。誰要是這么說,一準在撒謊。好消息是,就 ASP.NET 來說,ASP.NET,特別是版本 1.1 和即將發行的版本 2.0,集成了一些便于使用的內置防御屏障。
光是應用所有這些功能并不足以保護 Web 應用程序,使其免受任何可能和可預見的攻擊。但是,如果與其他防御技巧和安全策略相結合,內置的 ASP.NET 功能將可以構成一個強大的工具包,有助于確保應用程序在安全的環境中運行。
Web 安全性是各種因素的總和,是一種范圍遠超單個應用程序的策略的結果,這種策略涉及數據庫管理、網路配置,以及社會工程和 phishing。
本文的目的在于說明 ASP.NET 開發人員為了將安全標準保持到合理的高度,所應始終堅持的做法。這也就是安全性最主要的內容:保持警惕,永不完全放松,讓壞人越來越難以發起黑客攻擊。
下面我們來看看 ASP.NET 提供了哪些可以簡化這項工作的功能。
威脅的來源
在表 1 中,我匯總了最常見的 Web 攻擊類型,以及應用程序中可能導致這些攻擊得手的缺陷。
攻擊
攻擊的可能發起人
跨站點腳本 (XSS)
回顯到頁的不可信用戶輸入
SQL 注入
串連用戶輸入以形成 SQL 命令
會話劫持
會話 ID 猜測和失竊的會話 ID Cookie
一次單擊
通過腳本發送的未被察覺的 HTTP 張貼
隱藏域篡改
未檢查(且受信)的隱藏域被填充以敏感數據
![]() |
圖1.一次單擊攻擊 |
為什么是不抱懷疑的受害者?這是因為,這種情況下,服務器日志中所顯示的發出惡意請求的 IP 地址,是該受害者的 IP 地址。如前所述,這種工具并不像“經典”的 XSS 一樣常見(和易于發起);但是,它的性質決定了它的后果可能是災難性。如何應對它?下面,我們審視一下這種攻擊在 ASP.NET 環境下的工作機理。
除非操作編碼在 Page_Load 事件中,否則 ASP.NET 頁根本不可能在回發事件之外執行敏感代碼。要使回發事件發生,視圖狀態域是必需的。請牢記,ASP.NET 會檢查請求的回發狀態,并根據是否存在 _VIEWSTATE 輸入域,相應地設置 IsPostBack。因此,無論誰要向 ASP.NET 頁發送虛假請求,都必須提供一個有效的視圖狀態域。
一次單擊攻擊要想得手,黑客必須能夠訪問該頁。此時,有遠見的黑客會在本地保存該頁。這樣,他/她就可以訪問 _VIEWSTATE 域并使用該域,用舊的視圖狀態和其他域中的惡意值創建請求。問題是,這能行嗎?
為什么不能?如果攻擊者可以提供有效的身份驗證 cookie,黑客就可以進入,請求將被照常處理。服務器上根本不會檢查視圖狀態內容(當 EnableViewStataMac 為 off 時),或者只會檢查是否被篡改過。默認情況下,試圖狀態中沒有機制可以將該內容與特定的用戶關聯起來。攻擊者可以輕松地重用所獲取的視圖狀態,冒充另一個用戶合法地訪問該頁,以生成虛假請求。這正是 ViewStateUserKey 介入的地方。
如果選擇準確,該屬性可以將用戶特定的信息添加到視圖狀態。處理請求時,ASP.NET 會從視圖狀態中提取秘鑰,并將其與正在運行的頁的 ViewStateUserKey 進行比較。如果兩者匹配,請求將被認為是合法的;否則將引發異常。對于該屬性,什么值是有效的?
為所有用戶將 ViewStateUserKey 設置為常量字符串,相當于將它保留為空。您必須將它設置為對各個用戶都不同的值 — 用戶 ID,會話 ID 更好些。由于一些技術和社會原因,會話 ID 更為合適,因為會話 ID 不可預測,會超時失效,并且對于每個用戶都是不同的。
以下是一些在您的所有頁中都必不可少的代碼:
|
|
Cookie 和身份驗證
Cookie 之所以存在,是因為它們可以幫助開發人員達到一定目的。Cookie 充當了瀏覽器與服務器之間的一種持續性鏈接。特別是對于使用單次登錄的應用程序來說,失竊的 cookie 正是使得攻擊成為可能的罪魁禍首。這對于一次單擊攻擊來說一點沒錯。
要使用 Cookie,無需以編程方式顯式創建和讀取它們。如果您使用會話狀態且實現表單身份驗證,您會隱式地使用 Cookie。當然,ASP.NET 支持無 cookie 的會話狀態,而且,ASP.NET 2.0 還引入了無 cookie 的表單身份驗證。因此,理論上您可以在沒有 Cookie 的情況下使用這些功能。我并不是說您不再必須這么做了,但事實上這正是療法比疾病更糟的情形之一。無 Cookie 的會話,實際上將會話 ID 嵌入了 URL 中,這樣誰都可以看到。
與使用 Cookie 有關的潛在問題有哪些?Cookie 可能被盜(即被復制到黑客的計算機)和投毒(即被填充以惡意數據)。這些操作通常是即將發起的攻擊的前奏。如果被盜,Cookie 會“授權”外部用戶以您的名義連接到應用程序(并使用受保護的頁),這可能使黑客輕松地規避授權,并能夠執行角色和安全設置所允許受害者執行的任何操作。因此,身份驗證 Cookie 通常被賦予相對較短的生存期,即 30 分鐘。(請注意,即使瀏覽器的會話完成所需的時間更長,cookie 仍會過期。)發生失竊時,黑客有 30 分鐘的時限來嘗試攻擊。
可以將這個時限加長,以免用戶不得不過于頻繁地登錄。但請注意,這么做會將您自己置于危險境地。任何情況下,都應避免使用 ASP.NET 持續性 Cookie。它將導致 cookie 具有幾乎永久的生存期,最長可達 50 年!下面的代碼片段演示了如何輕松修改 cookie 的過期日期。
|
EnableViewStateMac
視圖狀態用于在對同一個頁的兩個連續請求之間保持控件的狀態。默認情況下,視圖狀態是 Base64 編碼的,并使用一個哈希值簽名,以防止篡改。除非更改默認的頁設置,否則不可能篡改視圖狀態。如果攻擊者修改了視圖狀態,甚至使用正確的算法重新生成了視圖狀態,ASP.NET 都會捕獲這些嘗試并引發異常。視圖狀態被篡改并不一定有害,雖然它修改了服務器控件的狀態 — 但可能成為造成嚴重感染的工具。因此,不 移除默認情況下進行的計算機身份驗證代碼 (MAC) 交叉檢查就異常重要。請參閱圖 2。
![]() |
圖 2. 啟用 EnableViewStateMac 時,使視圖狀態本身難以篡改的因素 |
啟用了 MAC 檢查時(默認情況),將對序列化的視圖狀態附加一個哈希值,該值是使用某些服務器端值和視圖狀態用戶秘鑰(如果有)生成的?;匕l視圖狀態時,將使用新的服務器端值重新計算該哈希值,并將其與存儲的值進行比較。如果兩者匹配,則允許請求;否則將引發異常。即使假設黑客具有破解和重新生成視圖狀態的能力,他/ 她仍需要知道服務器存儲的值才可以得出有效的哈希。具體說來,該黑客需要知道 machine.config 的 項中引用的計算機秘鑰。
默認情況下, 項是自動生成的,以物理方式存儲在 Windows Local Security Authority (LSA) 中。僅在 Web 場(此時視圖狀態的計算機秘鑰必須在所有的計算機上都相同)的情形下,您才應當在 machine.config 文件中將其指定為明文。
視圖狀態 MAC 檢查是通過一個名為 EnableViewStateMac 的 @Page 指令屬性控制的。如前所述,默認情況下,它被設置為 true。請永遠不要禁用它;否則將會使視圖狀態篡改一次單擊攻擊成為可能,并具有很高的成功概率。
ValidateRequest
跨站點腳本 (XSS) 對于很多經驗豐富的 Web 開發人員來說是老朋友了,它在 1999 年左右就已經出現了。簡單地說,XSS 利用代碼中的漏洞來將黑客的可執行代碼引入另一個用戶的瀏覽器會話中。如果被執行,注入的代碼可以執行多種不同的操作 — 獲取 Cookie 并將一個副本上載到黑客控制的 Web 站點,監視用戶的 Web 會話并轉發數據,修改被黑的頁的行為和外觀以使其提供錯誤的信息,甚至使自己變為持續性的,這樣用戶下一次返回該頁時,欺詐代碼會再次運行。
代碼中的哪些漏洞導致 XSS 攻擊成為可能?
XSS 利用的是動態生成 HTML 頁、但并不驗證回顯到頁的輸入的 Web 應用程序。這里的輸入 是指查詢字符串、Cookie 和表單域的內容。如果這些內容在未經適當性能檢查的情況下出現在網絡上,就存在黑客對其進行操作以在客戶端瀏覽器中執行惡意腳本的風險。(前面提到的一次單擊攻擊其實是 XSS 的一種新近變種。)典型的 XSS 攻擊會導致不抱懷疑的用戶點擊一條誘惑性鏈接,而該鏈接中嵌入了轉義的腳本代碼。欺詐代碼將被發送到一個存在漏洞且會毫不懷疑地輸出它的頁。用戶單擊一個看上去明顯安全的鏈接,最終導致將一些腳本代碼傳遞到存在漏洞的頁,這些代碼首先獲取用戶計算機上的所有 Cookie,然后將它們發送到黑客的 Web 站點。
請務必注意,XSS 不是一個特定于供應商的問題,因此并不一定會利用 Internet Explorer 中的漏洞。它影響目前市場上的所有 Web 服務器和瀏覽器。更應注意的是,沒有哪一個修補程序能夠修復這一問題。您完全可以保護自己的頁免受 XSS 攻擊,方法是應用特定的措施和合理的編碼實踐。此外,請注意,攻擊者并不需要用戶單擊鏈接就可以發起攻擊。
要防御 XSS,您必須從根本上確定哪些輸入是有效的,然后拒絕所有其他輸入。
阻止陰險的 XSS 攻擊的主要方法是向您的輸入(任何類型的輸入數據)添加一個設計合理、有效的驗證層。例如,某些情況下即使是原本無害的顏色(RGB 三色)也會將不受控制的腳本直接帶入頁中。
在 ASP.NET 1.1 中,@Page 指令上的 ValidateRequest 屬性被打開后,將檢查以確定用戶沒有在查詢字符串、Cookie 或表單域中發送有潛在危險性的 HTML 標記。如果檢測到這種情況,將引發異常并中止該請求。該屬性默認情況下是打開的;您無需進行任何操作就可以得到保護。如果您想允許 HTML 標記通過,必須主動禁用該屬性。
ValidateRequest不是萬能的藥方,無法替代有效的驗證層。它基本上通過應用一個正則表達式來捕獲一些可能有害的序列。
注 ValidateRequest 功能原本是有缺陷的,因此您需要應用一個修補程序它才能按預期工作。這樣的重要信息常常不為人們所注意。奇怪的是,我發現我的其中一臺計算機仍受該缺陷的影響。試試看!
沒有任何關閉 ValidateRequest 的理由。您可以禁用它,但必須有非常好的理由;其中一條這樣的理由可能是用戶需要能夠將某些 HTML 張貼到站點,以便得到更好的格式設置選項。這種情況下,您應當限制所允許的 HTML 標記的數目,并編寫一個正則表達式,以確保不會允許或接受任何其他內容。
以下是一些有助于防止 ASP.NET 遭受 XSS 攻擊的其他提示:
1)使用 HttpUtility.HtmlEncode 將危險的符號轉換為它們的 HTML 表示形式。
2)使用雙引號而不是單引號,這是因為 HTML 編碼僅轉義雙引號。
3)強制一個代碼頁以限制可以使用的字符數。
總之,使用但是不要完全信任 ValidateRequest 屬性,不要太過懶惰?;ㄐr間,從根本上理解 XSS 這樣的安全威脅,并規劃以一個關鍵點為中心的防御策略:所有的用戶輸入都是危險的。
數據庫角度
SQL 注入是另一種廣為人知的攻擊類型,它利用的是使用未篩選的用戶輸入來形成數據庫命令的應用程序。如果應用程序興高采烈地使用用戶鍵入表單域中的內容來創建 SQL 命令字符串,就會將您暴露在這一風險下:惡意用戶只需訪問該頁并輸入欺詐參數,就可以修改查詢的性質。您可以在此處了解更多有關 SQL 注入的信息。
要阻止 SQL 注入攻擊,有許多方法。以下介紹最常見的技巧。
1)確保用戶輸入屬于適當的類型,并遵循預期的模式(郵政編碼、身份證號,電子郵件等)。如果預期來自文本框的數字,請在用戶輸入無法轉換為數字的內容時阻止該請求。
2)使用參數化的查詢,使用存儲過程更好。
3)使用 SQL Server 權限來限制各個用戶可以對數據庫執行的操作。例如,您可能需要禁用 xp_cmdshell 或者將該操作的權限僅限于管理員。
如果使用存儲過程,可以顯著降低發生這種攻擊的可能性。實際上,有了存儲過程,您就無需動態地撰寫 SQL 字符串。此外,SQL Server 中將驗證所有參數是否具有指定的類型。雖然光是這些并不是百分百安全的技巧,但是加上驗證的話,將足以提高安全性。
更為重要的是,應確保只有經過授權的用戶才能夠執行可能具有嚴重后果的操作,如刪除表。這要求認真仔細地設計應用程序的中間層。好的技巧(不光是為了安全性)應把焦點集中在角色上。應當將用戶分組為各種角色,并為各個角色定義一個包含一組最少的權限的帳戶。
幾周前,Wintellect Web 站點受到一種很復雜的 SQL 注入的攻擊。那位黑客試圖創建并啟動一個 FTP 腳本來下載一個可能是惡意的可執行程序。幸運的是,這次攻擊失敗了?;蛘?,其實是強用戶驗證,使用存儲過程和使用 SQL Server 權限,導致了攻擊未能成功?
總而言之,您應當遵循這些指南,以避免被注入有害的 SQL 代碼:
1)使用盡可能少的權限運行,永遠不以“sa”身份執行代碼。
2)將訪問限制給內置的存儲過程。
3)首選使用 SQL 參數化查詢。
4)不通過字符串串連來生成語句,不回顯數據庫錯誤。
隱藏域
在傳統的 ASP 中,隱藏域是唯一一種在請求之間保持數據的方法。您需要在下一個請求中檢索的任何數據都被打包到隱藏的域中,并執行回程。如果有人在客戶端上修改了該域中存儲的值,會怎樣?只要文本是明文的,服務器端環境就無法測知這一情況。ASP.NET 中,頁和各個控件的 ViewState 屬性有兩個用途。一方面,ViewState 是跨請求保持狀態的方法;另一方面,ViewState 使您能夠在受保護的、不易篡改的隱藏域中存儲自定義值。
如圖2所示,視圖狀態被附加了一個哈希值,對于每條請求,都會檢查該值,以檢測是否發生了篡改。除少數幾種情況外,沒有任何理由要在 ASP.NET 中使用隱藏域。視圖狀態能夠以安全得多的方式實現相同的功能。前面開門見山地講到過,在明文的隱藏域中存儲敏感的值(如價格或信用卡詳細信息),相當于對黑客張開大門;視圖狀態甚至能夠使這種不好的做法比以前更為安全,因為視圖狀態具有數據保護機制。但是,請牢記,視圖狀態可以防止篡改,但是并不能保證保密性,除非使用加密 — 存儲在視圖狀態中的信用卡詳細信息無論如何都有風險。
在 ASP.NET 中,哪些情況下使用隱藏域是可接受的?當您生成需要將數據發送回服務器的自定義控件時。例如,假定您要創建一個支持重派列順序的新 DataGrid 控件。您需要在回發中將新的順序發送回服務器。如果不將這些信息存儲到隱藏域中,又可以存儲到哪里?
如果隱藏域為讀/寫域,即預期客戶端會寫入它,沒什么辦法能夠完全制止黑客攻擊。您可以嘗試哈?;蛘呒用茉撐谋?,但這并不能讓您合理地確信不會遭受黑客攻擊。此時,最好的防御就是讓隱藏域包含惰性和無害的信息。
此外,應當注意 ASP.NET 公開了一個鮮為人知的類,可用于編碼和哈希任何序列化的對象.該類為 LosFormatter,ViewState 實現用于創建回程到客戶端的編碼文本正是同一個類。
電子郵件和垃圾郵件
在本文結尾,請讓我指出,最常見的攻擊中至少有兩種(經典的 XSS 和一次單擊)通常是通過誘使不抱懷疑的受害者單擊誘惑性和欺騙性的鏈接來發起的。很多時候我們都可以在自己的收件箱中發現這樣的鏈接,雖然有反垃圾郵件過濾器。幾美元就可以買到大量電子郵件地址。用來生成這種列表的其中一種主要的技巧就是掃描 Web 站點上的公共頁,查找并獲取所有看上去像電子郵件的內容。
如果頁上顯示了電子郵件地址,很可能或早或晚這個地址都會被自動 Web 程序捕獲。真的嗎?當然,這要看該電子郵件是如何顯示的。如果硬編碼它,您輸定了。如果采用其他表示形式(如 dino-at-microsoft-dot-com),是否能夠騙過自動 Web 程序不太清楚,但能讓所有閱讀您的頁并想建立合法聯系的人光火,倒是一定的。
總體說來,您應當確定一種方法,將電子郵件動態地生成為 mailto 鏈接。
小結
有人懷疑 Web 可能是所有運行時環境中敵意最盛的嗎?根源在于誰都可以訪問 Web 站點,并嘗試向它傳遞好的或壞的數據。但是,創建不接受用戶輸入的 Web 應用程序,又有什么意義呢?
我們還是直面現實吧:無論您的防火墻如何強大,無論您如何頻繁地應用可用的修補程序,只要您運行的 Web 應用程序先天包含缺陷,攻擊者遲早都可以通過主通道,也就是端口 80,直接進入您的系統的最核心部分。
ASP.NET 應用程序與其他 Web 應用程序相較,既不更易受攻擊,也不更安全。安全性和漏洞同樣根植于編碼實踐、實際經驗和團隊合作。如果網絡不安全,那么任何應用程序都不安全;類似地,無論網絡如何安全,管理如何精良,如果應用程序存在缺陷,攻擊者總是能夠得手。
ASP.NET 的好處是提供了一些好的工具,只需少量工作,就可以將安全標準提升到可以接受的級別。當然,這并不是 足夠高的級別。不應純粹以來 ASP.NET 的內置解決方案,同樣也不應忽視它們。盡可能多地了解常見的攻擊。
(責任編輯:城塵)