前端安全之防範XSS實戰小結

因爲前段時間業務有接觸到富文本編輯器,且編輯器由用戶直接使用,因此不可避免須要對其涉及到的XSS防禦有所瞭解,所以對XSS防禦作一個實戰小結。

前言

XSS大部分前端coder都不會陌生,全稱:跨站腳本漏洞(Cross Site Scripting,簡寫做XSS)是Web應用程序在將數據輸出或者展現到網頁的時候存在問題,致使攻擊者能夠將對網站的正常功能形成影響甚至竊取或篡改用戶我的信息,其誘發的主要緣由是沒有針對用戶輸入來源的數據以及不可信數據源的過濾。本文將針對XSS進行簡單的歸類總結,而且提供相關的實戰處理手段。javascript

1、XSS 類型劃分

對於常見的XSS攻擊方式,大部分人再熟悉不過了,這裏再幫你們複習一下。css

一、反射性XSS:常見狀況是攻擊者經過構造一個惡意連接的形式,誘導用戶傳播和打開,因爲連接內所攜帶的參數會回顯於頁面中或做爲頁面的處理數據源,最終形成XSS攻擊。html

二、存儲型XSS:與前者不一樣,存儲型XSS是持久化的XSS攻擊方式,經過用戶輸入我的信息或者發表文章的方式將惡意代碼存儲於服務器端,當其餘用戶再次訪問頁面時觸發,形成XSS攻擊。前端

三、DOM based型XSS:一樣也是利用對數據源不可靠,缺少過濾,因爲開發過程當中,不可避免部分數據須要回填到DOM中,例如href、src、data-id等標籤屬性,而經過構造特殊的字符串閉合原有的DOM標籤,在其中注入script標籤的方式形成攻擊。java

2、XSS防範手段

針對XSS的防範,須要開發者養成意識,針對用戶輸入源的數據進行過濾,針對頁面不可信任的數據源也要作好過濾,相似於響應頭中的字段、url中的參數、refer等字段都是不可信的,而且結合其餘手段和方法,讓頁面更加安全可控。node

一、Htmlencode轉義特殊字符

大部分的論壇或者博客平臺,註冊帳號都會容許用戶填寫我的信息,包括暱稱、郵箱和個性簽名等,此類文本信息屬於非富文本類型,最多見的方法就是對尖括號標籤轉義成實體字符存儲,因爲是非富文本信息,而且以標籤內容的形式展現,若是不使用框架渲染,直接原生JS經過docoment.getElementById('xxx').innerText = 'your name'展現回頁面中便可。git

常見的處理方式以下:瀏覽器

const htmlEncode = function (handleString){
    return handleString
    .replace(/&/g,"&")
    .replace(/</g,"&lt;")
    .replace(/>/g,"&gt;")
    .replace(/ /g,"&nbsp;")
    .replace(/\'/g,"&#39;")
    .replace(/\"/g,"&quot;");
}

二、引入XSS庫針對用戶輸入源過濾,設置標籤白名單

前面說明了在非富文本的狀況下較爲快捷方便的處理方法,然而在大部分論壇中發帖發文章都是富文本編輯的形式,即最後回顯於頁面中的是一段HTML內容,不能單純以文本形式處理。安全

大部分的富文本編輯器原理,都是提供一個具有contenteditable屬性的dom元素,讓用戶對一段富文本進行編輯,其本質是對一段html進行處理,新增或刪除樣式等,最後經過回傳富文本框中html的方式提供給開發者,意味着咱們要容許用戶填充一段html於咱們的頁面中。而獲取到的html字符串咱們不能直接進行簡單的標籤替換,不然會致使原有的樣式丟失,最終展現在頁面中的也再也不是一篇排版精緻的文章,所以咱們要另尋他路。服務器

首先須要明確的是,不管是這份來自於富文本編輯器的html,仍是來自於最終用戶發起請求所獲取到的html,都是不可信的,意味着在前端進行過濾是沒有任何實際意義和價值的,由於攻擊者能夠輕易的僞造請求繞過限制,因此咱們須要在咱們的服務器端針對這段html進行過濾處理。針對html的標籤白名單過濾,不一樣的語言有不一樣的庫實現,這裏主要介紹nodejs中經常使用的標籤過濾庫,nodejs中經常使用的庫主要是xss和xss-filter,下面以xss庫的使用爲例:

import * as xss from 'xss';
function handleXss(content: Content) {
  // 設置HTML過濾器的白名單
  const options = {
    whiteList: {
      p: ['class', 'style'],
      em: ['class', 'style'],
      strong: ['class', 'style'],
      br: ['class', 'style'],
      u: ['class', 'style'],
      s: ['class', 'style'],
      blockquote: ['class', 'style'],
      li: ['class', 'style'],
      ol: ['class', 'style'],
      ul: ['class', 'style'],
      h1: ['class', 'style'],
      h2: ['class', 'style'],
      h3: ['class', 'style'],
      h4: ['class', 'style'],
      h5: ['class', 'style'],
      h6: ['class', 'style'],
      span: ['class', 'style'],
      div: ['class', 'style'],
      img: ['src', 'class', 'style', 'width'],
    },
  }; 
  // 自定義規則
  const myxss = new xss.FilterXSS(options);
  // 直接調用 myxss.process() 便可
  content.content = myxss.process(content.content);
  return content;
}

經過限定白名單,僅容許常見的文本展現標籤以及圖片img標籤進入白名單,這部分會再過濾後被保留,而且標籤內的class和style屬性也會被保留;其餘屬性和諸如script、iframe等標籤都會被直接過濾掉。 

這裏爲什麼要嚴格限定標籤的以及屬性的緣由,例如一個a標籤能夠經過href屬性注入一段相似這樣的js代碼:

<a href\="javascript:alert('xss')"\>click me</a\>

更有甚者會對注入的字符進行大小寫字符轉換,或轉義成等效字符,或插入空格或tab,但腳本仍然能正常運行,形成攻擊。

<p onclick="alert('xss')">this is a text</p>
<img src="xxx.com/test.jpg" onerror="alert('xss')"/>

其次,普通的標籤能夠直接經過綁定onclick的方式攻擊,即使是img、video等資源加載標籤,也能夠經過onload、onerror等事件注入腳本,可見針對標籤內屬性的過濾也是不可或缺的。

然而本覺得這樣已足夠,可是即便是隻開放了class和style屬性開放了,也是不安全的,關鍵在於style屬性,若是任由用戶自定義的話,能夠經過style屬性實現:點擊劫持(將元素鋪滿整個界面)、加載外域圖片、腳本注入甚至能夠給文章設置一些花裏胡哨的動畫:

<div style="position:absolute;top:0;left:0;width:2000px;height:2000px;z-index:9999;">
點擊劫持的元素,阻止頁面其餘操做</div>

<div style="background:url(javacript:alert('xss'))">
藉助style標籤注入腳本,大部分xss過濾庫會幫咱們過濾這部分腳本</div>

<div style="background:url(//xxx.com/H圖.jpg)">
偷偷加載其餘網站的小H圖,繞過過濾和審覈</div>

可見style屬性也不容忽視,所以咱們須要在option參數中額外爲style屬性設置白名單,確保style屬性安全可控:

...
   css: {
      whiteList: {
        color: true,
        'background-color': true,
      },
    },
...

其次爲了確保不加載非本域名下的圖片資源,咱們也能夠再這一層作一些針對img標籤的過濾:

...
    stripIgnoreTag: true,
    onTagAttr: (tag:string, name:string, value:string, isWhiteAttr:boolean) => {
      // 判斷img下的src屬性 若是非本域名下 返回空
      if (isWhiteAttr && tag === 'img' && name === 'src' && !checkLegal(value)) 
      {
        return '#';
      }
    },
    ...

三、cookie設置HttpOnly,配合token或驗證碼防範

針對信息源的過濾,針對不可信數據源的過濾,已經能達到初步的效果,但這遠遠不夠,畢竟沒有絕對的安全。

因爲大部分攻擊者會想要獲取到用戶的cookie去作別的壞事,因此咱們須要在http的響應頭set-cookie時設置httpOnly,讓瀏覽器知道不能經過document.cookie的方式獲取到cookie內容。

app.get('/', (req, res) => {
    if(req.cookies.isVisit) {
        console.log(req.cookies)
        res.send('歡迎再次光臨')
    } else {
        res.cookie('isVisit', 1, {maxAge: 3600 * 1000, httpOnly: true}) 
        res.send('歡迎初次光臨')
    }
})

雖然避免了攻擊者直接獲取到cookie,可是攻擊者仍然能夠頁面內發起別的請求,直接篡改用戶的信息,所以須要咱們配合token或者驗證碼的形式,防止攻擊者直接經過腳本的方式篡改用戶我的信息。而這個token相似於CSRF中咱們所須要的token,不過若是攻擊者仔細研究了代碼,而且知道的token在頁面中的來源,也是能夠直接獲取到token的,所以,相比之下驗證碼安全性更高。

四、設置CSP安全策略

除了針對數據源的嚴格過濾之外,CSP安全策略的限制也是主要的XSS防範手段之一,經過在頁面中設置容許加載的資源的來源,來嚴格限制頁面可加載的腳本以及圖片等資源,防止外部的腳本攻擊後注入其餘腳本以及內容。

CSP,內容安全策略,是一種基於內容的聲明式網絡應用程序機制,對緩解內容注入漏洞的危害很是有效。經過一系列指令告訴客戶端(如瀏覽器)被保護資源(如頁面)內只容許加載和執行指令集中限定的內容,相似白名單機制,不知足限定條件的資源和內容將被客戶端阻斷或不被執行。能夠經過兩種方式設置CSP,一種是meta標籤,一種是HTTP響應頭Content-Security-Policy:

指令及其說明:
default-src 定義資源默認加載策略
connect-src 定義Ajax、 WebSocket等加載策略
font-src 定義Font 加載策略
frame- src 定義Frame加載策略
img-src 定義圖片加載策略
media- src 定義<audio>、 <video> 等引用資源加載策略
object-src 定義 <applet>、 <embed>、 <object> 等引用資源加載策略
script-src 定義JS加載策略
style- src 定義CSS加載策略

對應的指令value能夠設置爲:(圖片引自:https://www.jianshu.com/p/4bad03d89c04
image.png
相信聰明的你看了這個規則一看就懂了,接下來,以meta標籤設置CSP爲例,咱們能夠以下設置,以此來限制對白名單外資源加載:

<meta http-equiv="Content-Security-Policy" content=
    "script-src 'self' *.qq.com *.cdn-go.cn; 
    img-src 'self' *.cdn-go.cn *.gtimg.cn data:;
    style-src 'unsafe-inline' *.cdn-go.cn; 
    media-src 'none'; 
    child-src *.qq.com">

五、其餘HTTP安全響應頭設置

除了CSP這個最經常使用最強勢的限制規則外,還有其餘的HTTP安全響應頭,若是心情好的話,能夠通通整上(滑稽):

1)、X-Frame-Options:X-Frame-Options HTTP 響應頭是用來給瀏覽器指示容許一個頁面能否在 <frame>, <iframe>或者 <object> 中展示的標記。網站可使用此功能,來確保本身網站的內容沒有被嵌到別人的網站中去,也從而避免了點擊劫持 (clickjacking) 的攻擊。
X-Frame-Options 有三個值:

DENY
表示該頁面不容許在 frame 中展現,即使是在相同域名的頁面中嵌套也不容許。
SAMEORIGIN
表示該頁面能夠在相同域名頁面的 frame 中展現。
ALLOW-FROM uri
表示該頁面能夠在指定來源的 frame 中展現。

2)、X-Content-Type-Options:X-Content-Type-Options 響應首部至關於一個提示標誌,被服務器用來提示客戶端必定要遵循在 Content-Type 首部中對 MIME 類型 的設定,而不能對其進行修改。這就禁用了客戶端的 MIME 類型嗅探行爲,(類型嗅探指的是瀏覽器在加載資源時,自動嗅探資源類型過程當中,致使執行了注入腳本),換句話說,也就是意味着網站管理員肯定本身的設置沒有問題。

3)、X-XSS-Protection:當檢測到跨站腳本攻擊 (XSS)時,瀏覽器將中止加載頁面。

然而這個響應頭因爲只有IE、谷歌等少部分瀏覽器支持,且不一樣瀏覽器的實現不一致,存在錯誤過濾行爲,甚至可能致使帶額外的漏洞,因此在實際狀況中較少使用。(爲避免錯誤過濾,部分門戶網站會將其值設置爲0)

X-XSS-Protection: 0
X-XSS-Protection: 1
X-XSS-Protection: 1; mode=block
X-XSS-Protection: 1; report=<reporting-uri>
0禁止XSS過濾。
1啓用XSS過濾(一般瀏覽器是默認的)。 若是檢測到跨站腳本攻擊,瀏覽器將清除頁面(刪除不安全的部分)。
1;mode=block啓用XSS過濾。 若是檢測到攻擊,瀏覽器將不會清除頁面,而是阻止頁面加載。
1; report=<reporting-URI> (Chromium only)啓用XSS過濾。 若是檢測到跨站腳本攻擊,瀏覽器將清除頁面並使用CSP report-uri指令的功能發送違規報告。

以推特網站爲例,其設置的響應頭以下:
x-content-type-options: nosniff
x-frame-options: DENY
x-xss-protection: 0

3、總結

本文主要總結概括了實踐過程當中針對XSS漏洞的防禦措施,首先針對用戶輸入源和不可信數據源須要咱們作好必要的過濾和校驗,其次,經過CSP安全策略和其餘HTTP響應頭的設置等進一步確保安全性。

只有綜合多種手段和方法才能確保本身的網站更加安全,若是不安全,那麼就刪git倉庫跑路。

謝謝觀看~

相關文章
相關標籤/搜索