[Web 安全] 瞭解XSS與防範

跨站腳本(XSS, Cross Site Script)攻擊指的是,攻擊者可讓某網站執行一段非法腳本。這種狀況很常見,好比提交一個表單用於修改用戶名,咱們能夠在文本框中輸入一些特殊字符,好比 <, >, ', " 等,檢查一下用戶名是否正確修改了。javascript

XSS 如何發生

XSS 必定是由用戶的輸入引發的,不管是提交表單、仍是點擊連接(參數)的方式,只要是對用戶的輸入不作任何轉義就寫到數據庫,或者寫到 htmljs 中,就頗有可能出錯。舉兩個例子。css

假設須要顯示一個新聞標題的列表,服務端渲染的話,用 jade 來實現的話也許是這樣的html

h1 娛樂速遞
ul
  each val in newslist
    li= val

newslist 就當是 ['新聞1', '新聞2', ...] 這樣格式的數組,若是直接把內容迭代渲染到 html 上的話,一旦某個新聞標題有特殊字符,好比標題中剛好包含一個 <p> 標籤,那麼它就不會顯示出來。前端

另外一個例子,用戶在寫博客,先不考慮實時保存吧,如今就僅僅須要預覽一下,那麼可能的代碼就是java

var preview = document.getElementById('#preview'),
    title   = document.getElementById('#blog-title'),
    content = document.getElementById('#blog-content');

preview.innerHTML = 
  '<h1>' + title.value + '</h1>' +
  '<pre>' + content.value + '</pre>';

這裏一樣是把用戶的輸入直接顯示在了 html 上,若是用戶的輸入中,正好輸入了 </h1>,把 <h1> 標籤提早結束,而後再輸入 <script>...</script> 就能夠直接執行 js 代碼了。數據庫

XSS 的發生至少須要一個條件,就是這些非法的腳本必須得在瀏覽器中解析。數組

從一個請求發出開始,到瀏覽器顯示內容,與 XSS 相關的有三個地方:URL、HTML、JavaScript。至於後臺方面,它分兩個功能,一個是將數據寫到數據庫,這時候也要對數據進行轉義,但不是XSS的範疇,它更可能是防止數據破壞 SQL 語句的結構;另外一個是從數據庫讀取數據,直接生成 HTML 或者以 JSON 的方式傳給前端,這些數據都必須轉義後才能顯示到瀏覽器中。瀏覽器

HTML 特殊字符

HTML 自己是一個文本文檔,但在瀏覽器中卻能夠顯現得花樣百出,是由於不少字符對於瀏覽器來講是有特殊含義的,好比在 <script> 中的內容,瀏覽器會作一些動畫等等。那麼對這些特殊字符進行轉義,就意味着讓瀏覽器對待它們的時候,就像普通字符同樣,好比 &lg;script&gt; 這段文字在瀏覽器中就會正常顯示爲 <script>動畫

當咱們在代碼中生成 HTML 時,必定要注意,變量是否轉義了。像這種網站

el.innerHTML = title.value;

就是很是危險的。由於輸入框的內容來源於用戶,而用戶的輸入是不可靠的。不管是前端仍是後臺,必定要有一個相似於 escapeHTML 的方法,而後在代碼中這樣使用

el.innerHTML = escapeHTML(title.value);

這邊貼一段簡單的用來轉義 HTML 的 JavaScript 方法

function encodeHTML (a) {
  return String(a)
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
};

那麼有哪些字符須要轉義呢?這裏列了一些常見的。

" --> &#34;
# --> &#35;
$ --> &#36;
& --> &#38;
' --> &#39;
( --> &#40;
) --> &#41;
; --> &#59;
< --> &#60;
> --> &#62;

在 escapeHTML 方法中,我使用了別名的方式轉義,由於它比較容易記一點。不管是別名仍是十六進制,它們表示的含義都是同樣的,好比 &amp;&#38; 都表示 & 符號。想要看更具體的列表能夠參考這個網站

在瀏覽器收到 HTML 以後,首先會對全部的內容進行解碼,它會把全部能識別的編碼符號,解碼成字面值。好比有

<p>my name is&#58;&#32;<a href="http&#58;&#47;&#47;www.jchen.cc">名一</a></p>

通過瀏覽器解碼就變成

<p>my name is: <a href="http://www.jchen.cc">名一</a></p>

這裏要說的是,瀏覽器只會對兩個地方解碼,一個是標籤的內容(即 textContent,除了 <script><style> 標籤),另外一個是標籤的屬性值。對於屬性名是不會解碼的。

URL

早些時候,服務端還不支持在 URL 中直接傳輸 Unicode,好比 http://jchen.cc/find?q=你好 這樣的地址,服務端沒法識別「你好」這個值,因此必須編碼以後進行傳輸。

那麼對於 URL,咱們只須要對參數的值進行編碼就能夠了。好比上面這個連接,編碼以後就是 http://jchen.cc/find?q=%E4%BD%A0%E5%A5%BD

若是對整個 URL 編碼,那麼連接就無效了。

編碼的方式很簡單,瀏覽器提供了全局的 encodeURI 方法,調用以後就能夠實現轉義了。

有一點很重要,encodeURI 是不會轉義 :, /, ?, &, = 這些在 URL 中有特殊含義的字符的,那麼若是有個參數正好包含了這些字符,就不會轉義,好比

encodeURI('http://jchen.cc/login?name=名一&from=http://other.com'); 

// -> http://jchen.cc/login?name=%E5%90%8D%E4%B8%80&from=http://other.com

from 參數的值並無轉義,這時候,就須要用到另外一個方法 encodeURIComponent

var param = encodeURIComponent('http://other.com');
encodeURI('http://jchen.cc/login?name=名一&from=') + param;

// -> http://jchen.cc/login?name=%E5%90%8D%E4%B8%80&from=http%3A%2F%2Fother.com

因此結論就是,若是要對整個 URL 進行轉義,使用 encodeURI,若是對參數的值進行轉義,使用 encodeURIComponent

當動態生成的連接地址須要賦值給 href 或者 src 屬性時,須要對這些地址進行 URL 轉義。固然,若是服務端支持在 URL 中包含 UTF-8 的字符的話,其實不轉義也不會錯,這就是爲何咱們平時不會太注意對錶單和 URL 參數進行轉義的緣由,由於服務端表現良好。

JavaScript 特殊字符

JS 中的轉義都是經過反斜槓完成,有三種類型,以 '" 爲例

  • 直接反斜槓 --> \'\"

  • 十六進制 --> \x22\x27

  • Unicode --> \u0022\u0027

通常狀況下能夠直接經過反斜槓轉義,但有些字符咱們不知道怎麼輸入,很常見的好比 Web Font,在 CSS 中能夠看到相似這樣的代碼

.glyphicon-home::before {
    content: "";
}

那個 content 中的值能夠經過十六進制或者 Unicode 的方式來代替。

JS 轉義通常用於顯示用戶輸入的時候,好比用戶輸入了反斜槓,須要顯示時,就必須 alert('\\');

解碼順序

當瀏覽器進行繪製時,首先會對 HTML 進行解碼,而後是 URL,最後是執行 JS 時對它進行解碼。

如今考慮這三種編碼同時存在的狀況

<a href="javascript&#58;&#32;alert('\<http&#58;&#47;&#47;jchen.cc/find?q=%E4%BD%A0%E5%A5%BD\>');">click</a>

首先是 HTML 解碼,結果爲

<a href="javascript: alert('\<http://jchen.cc/find?q=%E4%BD%A0%E5%A5%BD\>');">click</a>

而後是 URL 解碼,結果爲

<a href="javascript: alert('\<http://jchen.cc/find?q=你好\>');">click</a>

最後是 JS 解碼,結果爲

<a href="javascript: alert('<http://jchen.cc/find?q=你好>');">click</a>

單擊連接後,應該會出現一個彈窗,內容是 <http://jchen.cc/find?q=你好>

本文更多的是介紹如何防止XSS的發生,而不是它的危害。核心就是用適當的方法對 HTML, JS 進行轉義。

相關文章
相關標籤/搜索