利用ASP.NET的內(nèi)置功能抵御Web攻擊(上)

2010-08-28 10:49:01來源:西部e網(wǎng)作者:

  如果您正在閱讀本文,可能就不需要再向您灌輸 Web 應用程序中的安全性愈來愈重要這一事實了。您需要的可能是一些有關如何在 ASP.NET 應用程序中實現(xiàn)安全性的實際建議。壞消息是,沒有任何開發(fā)平臺 — 包括 ASP.NET在內(nèi) — 能夠保證一旦采用了該平臺,您就能夠編寫百分百安全的代碼。誰要是這么說,一準在撒謊。好消息是,就 ASP.NET 來說,ASP.NET,特別是版本 1.1 和即將發(fā)行的版本 2.0,集成了一些便于使用的內(nèi)置防御屏障。

  光是應用所有這些功能并不足以保護 Web 應用程序,使其免受任何可能和可預見的攻擊。但是,如果與其他防御技巧和安全策略相結(jié)合,內(nèi)置的 ASP.NET 功能將可以構(gòu)成一個強大的工具包,有助于確保應用程序在安全的環(huán)境中運行。

  Web 安全性是各種因素的總和,是一種范圍遠超單個應用程序的策略的結(jié)果,這種策略涉及數(shù)據(jù)庫管理、網(wǎng)路配置,以及社會工程和 phishing。

  本文的目的在于說明 ASP.NET 開發(fā)人員為了將安全標準保持到合理的高度,所應始終堅持的做法。這也就是安全性最主要的內(nèi)容:保持警惕,永不完全放松,讓壞人越來越難以發(fā)起黑客攻擊。

  下面我們來看看 ASP.NET 提供了哪些可以簡化這項工作的功能。

  威脅的來源
  在表 1 中,我匯總了最常見的 Web 攻擊類型,以及應用程序中可能導致這些攻擊得手的缺陷。

  攻擊 攻擊的可能發(fā)起人
  跨站點腳本 (XSS)
  回顯到頁的不可信用戶輸入

  SQL 注入
  串連用戶輸入以形成 SQL 命令

  會話劫持
  會話 ID 猜測和失竊的會話 ID Cookie

  一次單擊
  通過腳本發(fā)送的未被察覺的 HTTP 張貼

  隱藏域篡改
  未檢查(且受信)的隱藏域被填充以敏感數(shù)據(jù)

  常見的 Web 攻擊

  列表中顯現(xiàn)出來的關鍵性事實有哪些?在我看來,起碼有以下三點:

  • 無論您何時將何種用戶輸入插入瀏覽器的標記中,您都潛在地將自己暴露在了代碼注入攻擊(任何 SQL 注入和 XSS 變種)之下。

  • 必須以安全的方式實現(xiàn)數(shù)據(jù)庫訪問,就是說,應當為數(shù)據(jù)庫使用盡可能少的權限,并通過角色來劃分各個用戶的職責。

  • 永遠都不通過網(wǎng)絡發(fā)送敏感數(shù)據(jù)(更別說是明文了),并且必須以安全的方式將敏感數(shù)據(jù)存儲在服務器上。

有意思的是,上面的三點分別針對的是 Web 安全性的三個不同方面,而這三個方面結(jié)合起來,才是唯一的一種生成防攻擊、防篡改應用程序的合理方式。Web 安全性的各個層面可以總結(jié)如下:

  • 編碼實踐:數(shù)據(jù)驗證、類型和緩沖區(qū)長度檢查,防篡改措施

  • 數(shù)據(jù)訪問策略:使用決策來保護可能最弱的帳戶,使用存儲過程或者至少是參數(shù)化的命令。

  • 有效的存儲和管理:不將關鍵性數(shù)據(jù)發(fā)送到客戶端,使用哈希代碼來檢測操作,對用戶進行身份驗證并保護標識,應用嚴格的密碼策略

  如您所看到的,只有可以通過開發(fā)人員、架構(gòu)師和管理員的共同努力,才可以產(chǎn)生安全的應用程序。請不要假定您能夠以其他方式達到同樣目的。

  編寫 ASP.NET 應用程序時,您并不是獨自面對黑客大軍:唯一的武器是通過自己的大腦、技能和手指鍵入的代碼行。ASP.NET 1.1 和更高版本都會施加援手,它們具有一些特定的功能,可以自動提高防御以上列出的某些威脅的屏障。下面我們對它們進行詳細的檢視。

  ViewStateUserKey

  從 ASP.NET 1.1 開始引入,ViewStateUserKey 是 Page 類的一個字符串屬性,只有很少數(shù)開發(fā)人員真正熟悉該屬性。為什么呢?讓我們看看文檔中是怎么說的。

  在與當前頁相關聯(lián)的視圖狀態(tài)變量中將一個標識符分配給單個用戶

  除了有些累贅,這個句子的意思相當清楚;但是,您能老老實實地告訴我,它說明了該屬性原本的用途嗎?要理解 ViewStateUserKey 的角色,您需要繼續(xù)往下讀,直到 Remarks 部分。

  該屬性有助于防止一次單擊攻擊,因為它提供了附加的輸入以創(chuàng)建防止視圖狀態(tài)被篡改的哈希值。換句話說,ViewStateUserKey 使得黑客使用客戶端視圖狀態(tài)的內(nèi)容來準備針對站點的惡意張貼困難了許多。可以為該屬性分配任何非空的字符串,但最好是會話 ID 或用戶的 ID。為了更好地理解這個屬性的重要性,下面我們簡短介紹一下一次單擊攻擊的基本知識。

  一次單擊攻擊包括將惡意的 HTTP 表單張貼到已知的、易受攻擊的 Web 站點。之所以稱為“一次單擊”,是因為它通常是以受害者不經(jīng)意的單擊通過電子郵件發(fā)送的或者在擁擠的論壇中瀏覽時發(fā)現(xiàn)的誘惑性鏈接而開始的。通過點擊該鏈接,用戶無意中觸發(fā)了一個遠程進程,最終導致將惡意的 <form> 提交到一個站點。大家都坦白些吧:您真能告訴我,您從未因為好奇而單擊過 Click here to win $1,000,000 這樣的鏈接嗎?顯然,并沒有什么糟糕的事情發(fā)生在您身上。讓我們假定的確是這樣的;您能說 Web 社區(qū)中的所有其他人都幸免于難了嗎?誰知道呢。

要想成功,一次單擊攻擊需要特定的背景條件:

  • 攻擊者必須充分了解該有漏洞的站點。這是可能的,因為攻擊者可以“勤奮地”研究該文件,或者他/她是一位憤怒的內(nèi)部人員(例如,被解雇而又不誠實的雇員)。因此,這種攻擊的后果可能是極其嚴重的。

  • 站點必須是使用 Cookie(如果是持續(xù)性 Cookie,效果更好)來實現(xiàn)單次登錄,而攻擊者曾經(jīng)收到過有效的身份驗證 cookie。

  • 該站點的某些用戶進行了敏感的事務。

  • 攻擊者必須能夠訪問目標頁。


  前已提及,攻擊包括將惡意的 HTTP 表單提交到等待表單的頁。可以推知,該頁將使用張貼來的數(shù)據(jù)執(zhí)行某些敏感操作?上攵,攻擊者清楚地了解如何使用各個域,并可以想出一些虛假的值來達到他的目的。這通常是目標特定的攻擊,而且由于它所建立的三角關系,很難追本溯源 — 即黑客誘使受害者單擊該黑客站點上的一個鏈接,而這又會導致惡意代碼被張貼到第三個站點。(請參閱圖 1。)

圖 1. 一次單擊攻擊

  為什么是不抱懷疑的受害者?這是因為,這種情況下,服務器日志中所顯示的發(fā)出惡意請求的 IP 地址,是該受害者的 IP 地址。如前所述,這種工具并不像“經(jīng)典”的 XSS 一樣常見(和易于發(fā)起);但是,它的性質(zhì)決定了它的后果可能是災難性。如何應對它?下面,我們審視一下這種攻擊在 ASP.NET 環(huán)境下的工作機理。

除非操作編碼在 Page_Load 事件中,否則 ASP.NET 頁根本不可能在回發(fā)事件之外執(zhí)行敏感代碼。要使回發(fā)事件發(fā)生,視圖狀態(tài)域是必需的。請牢記,ASP.NET 會檢查請求的回發(fā)狀態(tài),并根據(jù)是否存在 _VIEWSTATE 輸入域,相應地設置 IsPostBack。因此,無論誰要向 ASP.NET 頁發(fā)送虛假請求,都必須提供一個有效的視圖狀態(tài)域。

  一次單擊攻擊要想得手,黑客必須能夠訪問該頁。此時,有遠見的黑客會在本地保存該頁。這樣,他/她就可以訪問 _VIEWSTATE 域并使用該域,用舊的視圖狀態(tài)和其他域中的惡意值創(chuàng)建請求。問題是,這能行嗎?

  為什么不能?如果攻擊者可以提供有效的身份驗證 cookie,黑客就可以進入,請求將被照常處理。服務器上根本不會檢查視圖狀態(tài)內(nèi)容(當 EnableViewStataMac 為 off 時),或者只會檢查是否被篡改過。默認情況下,試圖狀態(tài)中沒有機制可以將該內(nèi)容與特定的用戶關聯(lián)起來。攻擊者可以輕松地重用所獲取的視圖狀態(tài),冒充另一個用戶合法地訪問該頁,以生成虛假請求。這正是 ViewStateUserKey 介入的地方。
如果選擇準確,該屬性可以將用戶特定的信息添加到視圖狀態(tài)。處理請求時,ASP.NET 會從視圖狀態(tài)中提取秘鑰,并將其與正在運行的頁的 ViewStateUserKey 進行比較。如果兩者匹配,請求將被認為是合法的;否則將引發(fā)異常。對于該屬性,什么值是有效的?

  為所有用戶將 ViewStateUserKey 設置為常量字符串,相當于將它保留為空。您必須將它設置為對各個用戶都不同的值 — 用戶 ID,會話 ID 更好些。由于一些技術和社會原因,會話 ID 更為合適,因為會話 ID 不可預測,會超時失效,并且對于每個用戶都是不同的。

  以下是一些在您的所有頁中都必不可少的代碼:


  void Page_Init (object sender, EventArgs e) {
ViewStateUserKey = Session.SessionID;
:
}



  為了避免重復編寫這些代碼,您可以將它們固定在從 Page 派生的類的 OnInit 虛擬方法中。(請注意,您必須在 Page.Init 事件中設置此屬性。)

protected override OnInit(EventArgs e) { 
base.OnInit(e);
ViewStateUserKey = Session.SessionID;
}



  總體說來,使用基 page 類始終都不失為一件好事,我在 Build Your ASP.NET Pages on a Richer Bedrock 一文中已經(jīng)進行了說明。如果您要了解更多有關一次單擊攻擊者的伎倆的信息,可以在 aspnetpro.com 找到一篇非常好的文章。 

Cookie 和身份驗證

  Cookie 之所以存在,是因為它們可以幫助開發(fā)人員達到一定目的。Cookie 充當了瀏覽器與服務器之間的一種持續(xù)性鏈接。特別是對于使用單次登錄的應用程序來說,失竊的 cookie 正是使得攻擊成為可能的罪魁禍首。這對于一次單擊攻擊來說一點沒錯。

  要使用 Cookie,無需以編程方式顯式創(chuàng)建和讀取它們。如果您使用會話狀態(tài)且實現(xiàn)表單身份驗證,您會隱式地使用 Cookie。當然,ASP.NET 支持無 cookie 的會話狀態(tài),而且,ASP.NET 2.0 還引入了無 cookie 的表單身份驗證。因此,理論上您可以在沒有 Cookie 的情況下使用這些功能。我并不是說您不再必須這么做了,但事實上這正是療法比疾病更糟的情形之一。無 Cookie 的會話,實際上將會話 ID 嵌入了 URL 中,這樣誰都可以看到。

  與使用 Cookie 有關的潛在問題有哪些?Cookie 可能被盜(即被復制到黑客的計算機)和投毒(即被填充以惡意數(shù)據(jù))。這些操作通常是即將發(fā)起的攻擊的前奏。如果被盜,Cookie 會“授權”外部用戶以您的名義連接到應用程序(并使用受保護的頁),這可能使黑客輕松地規(guī)避授權,并能夠執(zhí)行角色和安全設置所允許受害者執(zhí)行的任何操作。因此,身份驗證 Cookie 通常被賦予相對較短的生存期,即 30 分鐘。(請注意,即使瀏覽器的會話完成所需的時間更長,cookie 仍會過期。)發(fā)生失竊時,黑客有 30 分鐘的時限來嘗試攻擊。

  可以將這個時限加長,以免用戶不得不過于頻繁地登錄;但請注意,這么做會將您自己置于危險境地。任何情況下,都應避免使用 ASP.NET 持續(xù)性 Cookie。它將導致 cookie 具有幾乎永久的生存期,最長可達 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)身份驗證 Cookie 的生存期。 

關鍵詞:ASP.NET

贊助商鏈接: