天天看點

如何避免XSS攻擊

前言

XSS 攻擊:即跨站腳本攻擊,它是 Web 程式中常見的漏洞。原理是攻擊者往 Web 頁面裡插入惡意的腳本代碼(css 代碼、Javascript 代碼等),當使用者浏覽該頁面時,嵌入其中的腳本代碼會被執行,進而達到惡意攻擊使用者的目的,如盜取使用者 cookie、破壞頁面結構、重定向到其他網站等。

預防:

1、擷取使用者的輸入,不用innerHtml,用innerText.

2、預防 XSS 的核心是必須對輸入的資料做過濾處理。對使用者的輸入進行過濾,如對& < > " ’ /等進行轉義。

XSS案例

說明:這裡用一個XSS案例更好的說明XSS攻擊,重點感謝美團技術團隊的案例支援。   
           

某天,公司需要一個搜尋頁面,根據 URL 參數決定關鍵詞的内容。小明很快把頁面寫好并且上線。代碼如下:

<input type="text" value="<%= getParameter("keyword") %>">
<button>搜尋</button>
<div>
  您搜尋的關鍵詞是:<%= getParameter("keyword") %>
</div>
           

然而,在上線後不久,小明就接到了安全組發來的一個神秘連結:

http://xxx/search?keyword="><script>alert('XSS');</script>
           

小明帶着一種不祥的預感點開了這個連結[請勿模仿,确認安全的連結才能點開]。果然,頁面中彈出了寫着"XSS"的對話框。

可惡,中招了!小明眉頭一皺,發現了其中的奧秘:

當浏覽器請求 http://xxx/search?keyword=">,拼接到 HTML 中傳回給浏覽器。形成了如下的 HTML:

<input type="text" value=""><script>alert('XSS');</script>">
<button>搜尋</button>
<div>
  您搜尋的關鍵詞是:"><script>alert('XSS');</script>
</div>
           

浏覽器無法分辨出 是惡意代碼,因而将其執行。

這裡不僅僅 div 的内容被注入了,而且 input 的 value 屬性也被注入, alert 會彈出兩次。

面對這種情況,我們應該如何進行防範呢?

其實,這隻是浏覽器把使用者的輸入當成了腳本進行了執行。那麼隻要告訴浏覽器這段内容是文本就可以了。

聰明的小明很快找到解決方法,把這個漏洞修複

<input type="text" value="<%= escapeHTML(getParameter("keyword")) %>">
<button>搜尋</button>
<div>
  您搜尋的關鍵詞是:<%= escapeHTML(getParameter("keyword")) %>
</div>
           

經過了轉義函數的處理後,最終浏覽器接收到的響應為:

<input type="text" value="&quot;&gt;&lt;script&gt;alert(&#x27;XSS&#x27;);&lt;&#x2F;script&gt;">
<button>搜尋</button>
<div>
  您搜尋的關鍵詞是:&quot;&gt;&lt;script&gt;alert(&#x27;XSS&#x27;);&lt;&#x2F;script&gt;
</div>
           

通過這個事件,學習到了如下知識:

1.通常頁面中包含的使用者輸入内容都在固定的容器或者屬性内,以文本的形式展示。
 2.攻擊者利用這些頁面的使用者輸入片段,拼接特殊格式的字元串,突破原有位置的限制,形成了代碼片段。 
 3.攻擊者通過在目标網站上注入腳本,使之在使用者的浏覽器上運作,進而引發潛在風險。
 4. 通過 HTML 轉義,可以防止 XSS 攻擊。<span style="color:red">[事情當然沒有這麼簡單啦!請繼續往下看]</span>。
           

注意特殊的 HTML 屬性、JavaScript API

自從上次事件之後,小明會小心的把插入到頁面中的資料進行轉義。而且他還發現了大部分模闆都帶有的轉義配置,讓所有插入到頁面中的資料都預設進行轉義。這樣就不怕不小心漏掉未轉義的變量啦,于是小明的工作又漸漸變得輕松起來。

但是,作為導演的我,不可能讓小明這麼簡單、開心地改 Bug

不久,小明又收到安全組的神秘連結:

http://xxx/?redirect_to=javascript:alert(‘XSS’)。小明不敢大意,趕忙點開頁面。然而,頁面并沒有自動彈出萬惡的“XSS”。

小明打開對應頁面的源碼,發現有以下内容:

<a href="<%= escapeHTML(getParameter(" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" redirect_to")) %>">跳轉...</a>
           

這段代碼,當攻擊 URL 為 http://xxx/?redirect_to=javascript:alert(‘XSS’),服務端響應就成了:

<a href="javascript:alert(&#x27;XSS&#x27;)" target="_blank" rel="external nofollow" >跳轉...</a>
           

雖然代碼不會立即執行,但一旦使用者點選 a 标簽時,浏覽器會就會彈出“XSS”。

在這裡,使用者的資料并沒有在位置上突破我們的限制,仍然是正确的 href 屬性。但其内容并不是我們所預期的類型。

原來不僅僅是特殊字元,連 javascript: 這樣的字元串如果出現在特定的位置也會引發 XSS 攻擊。

小明眉頭一皺,想到了解決辦法:

// 禁止 URL 以 "javascript:" 開頭
xss = getParameter("redirect_to").startsWith('javascript:');
if (!xss) {
  <a href="<%= escapeHTML(getParameter(" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" redirect_to"))%>">
    跳轉...
  </a>
} else {
  <a href="/404" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
    跳轉...
  </a>
}
           

隻要 URL 的開頭不是 javascript:,就安全了吧?

安全組随手又扔了一個連接配接:http://xxx/?redirect_to=jAvascRipt:alert(‘XSS’)

,在判斷 URL 開頭是否為 javascript: 時,先把使用者輸入轉成了小寫,然後再進行比對。

不過,所謂“道高一尺,魔高一丈”。面對小明的防護政策,安全組就構造了這樣一個連接配接:

http://xxx/?redirect_to=%20javascript:alert('XSS')
           

%20javascript:alert('XSS')

經過 URL 解析後變成 javascript:alert(‘XSS’),這個字元串以空格開頭。這樣攻擊者可以繞過後端的關鍵詞規則,又成功的完成了注入。

最終,小明選擇了白名單的方法,徹底解決了這個漏洞:

// 根據項目情況進行過濾,禁止掉 "javascript:" 連結、非法 scheme 等
allowSchemes = ["http", "https"];

valid = isValid(getParameter("redirect_to"), allowSchemes);

if (valid) {
  <a href="<%= escapeHTML(getParameter(" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" redirect_to"))%>">
    跳轉...
  </a>
} else {
  <a href="/404" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
    跳轉...
  </a>
}
           

通過這個事件,小明學習到了如下知識:

1、做了 HTML 轉義,并不等于高枕無憂。

2、對于連結跳轉,如 <a href=“xxx” 或 location.href=“xxx”,要檢驗其内容,禁止以 javascript: 開頭的連結,和其他非法的 scheme。

作者:狂歡
           

繼續閱讀