Http - Content Security Policy

Content Security Policy (簡稱 CSP) 是 W3C 所提出來的一個資訊安全的 Http Header,主要是用來防止XSS 或 Data Injection 的攻擊。原理很簡單,就是在 CSP 的 Header 定義信任的規則,這些規則就是根據每一種網路資源,指定所信任的來源。舉例來說,我可以指定 script 檔的來源只能從本身或者是 cdnjs.com 的 Domain 來的,其餘的都會被擋掉。然而我也可以指定 font 字形來源只能從 font-awesome 的官網載入。其他像是 style 或 media, audio 這些網路資源都可以指定特定的信任來源。

使用方式也很簡單,就是在 Http Response Header 裏頭加入 Content-Security-Policy 的 Header,然後內容為 Policy 的名稱字串,後面會說明有哪些 Policy,以下為 Header 的格式:
Content-Security-Policy: <policy> <source> <source>; <policy> <source> <source>
這邊要注意的是每個 Policy 之間是用分號區隔
使用範例如下:
Content-Security-Policy: default-src 'self'' *.capital.com.tw; style-src https://www.w3schools.com
上面範例的意思為:樣式來源只能來自 https://www.w3schools.com,其他資源來源只能從本站或 Domain 為 capital.com.tw 的網站。

CSP 很多種 Policy,有 child-src, connect-src, default-src, font-src, frame-src, img-src, manifest-src, media-src, object-src, script-src, style-src, worker-src,限量會在這邊介紹幾種較常用的 Policy。

default-src

為預設的 Policy,在 CSP 的 Policy 中最好要載入此 Policy,主要作用為當 Browser 解析上述所提到的所有 Policy 時,如果沒有特別設置時,該 Policy 會讀取 default-src 為設定值。舉例來說,假如我只有設置 default-src 'self' *.capital.com.tw,那所有資源的來源只能由本站或從 capital.com.tw 的 Domain 取得。

frame-src

控制頁面中的<frame>, <iframe> 載入的內容來源。

img-src

控制頁面中圖片<img>載入的來源位置。

media-src

控制頁面中音樂<audio>或視訊<video>的載入來源位置。

script-src

控制頁面中可執行的 script 來源。這個 Policy 比較重要,因為只要控制的好就能夠避免 XSS 和 Data Injection 的攻擊。舉例來說,我們設置 CSP 為 script-src 'self',有一個 <button onclick="btnClick()">Click me</button>,當我們按下去的時候是不會作用的,因為它執行的是 inline script,但是被 CSP 限制 script 的來源只能從 https://test.com.tw/,所以就會被 block 住,這種情況不能再 HTML 的 DOM 上進行 Event 的綁定,只能從 script 裏頭用 addEventListener 來綁定事件。

style-src

控制頁面載入的樣式資源的來源。這個 Policy 也蠻重要的,也可以防止 XSS 與 Data Injection 的攻擊。假設我們設定 style-src https://www.test.com.tw/,那不管我們使用<link>的方式或用 @import 的方式都會被限制來源只能來自 https://www.test.com.tw,另外在頁面上也無法使用<style>自訂樣式。

為了更加了解實際的執行狀況,限量會用一個簡單的頁面來測試 CSP。前面提到 CSP 是加在 Http Response Header 上,但其實還有另外一種方式可以加上 CSP,就是直接在 HTML頁面上加上 <meta http-equiv="Content-Security-Policy" content="default-src ...">

首先先來看看一個簡單的測試頁面,這個頁面的樣式直接從 w3school 載入 W3.CSS,而 script 則從 cdnjs 載入 jQuery,另外也在本機載入自己的 testjs.js。執行起來如下:
如果我們在<head>上面加上CSP的<meta>如下:
<meta http-equiv="Content-Security-Policy" content="default-src 'self';">

來看看會發生什麼事:
你會發現不管是樣式或 script 都被擋住了,錯誤訊息大概就是跟你講說你違反了 CSP,所以不給你載。再來我們來修一下 CSP:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src https://www.w3schools.com/; script-src 'self' https://cdnjs.cloudflare.com">
再來看看發生什麼事,

可以發現樣式跟 script 都載進來了,但是當我按下 Inline JS 的 button 會發生錯誤,那是因為這個 button 是直接在 HTML 上綁定 onclick Event,而在定義的 CSP 中無法允許執行 inline script,所以才會被擋。解法可以像前面 script-src 所提到的,直接在 testjs.js 裏頭用 addEventListener 的方式來綁定。或者可以用另一種方式,就是在 script-src 加入 'unsafe-inline' 的 source,但是建議不要,這樣就可能導致 XSS 或 Data Injection 發生,那使用 CSP 就沒什麼意義了。

以下為最終的範例程式碼:

TestCST.html
<!DOCTYPE html>
<html>
    <head>
        <title>Test CSP</title>
        
        <!--加入此行-->
        <meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src https://www.w3schools.com/; script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com">
       
        <link rel="stylesheet" href="https://www.w3schools.com/lib/w3.css">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
        <script src="testjs.js"></script>
    </head>
    <body>
        <h1 class="w3-panel w3-green">W3 CSS Stype</h1>
        <button id="self_btn" class="w3-button w3-blue">Self JS</button>
        <button class="w3-button w3-red" onclick="inlineBtnClick()">Inline JS</button>
    </body>
</html>

testjs.js
function inlineBtnClick() {
    alert('Inline invoked.');
}

$(function() {
    $('#self_btn').click(() => {
        alert('Self invoked.');
    });
});


CSP 還有其他的 Policy 限量沒有仔細的說明並實際操作,這個部份如果有興趣的話可以直接到 MDN 上深入研究,至少使用 style-src 與 script-src 就能避掉大多的 XSS 攻擊了。





參考來源:

MDN - Content Security Policy






留言