解決載入 js 腳本被 block:csp 的問題

Eason Lin
9 min readDec 7, 2024

--

Photo by Zdeněk Macháček on Unsplash

幾天前協助幫公司產品導入一個外部 SDK 時,碰到了 block:csp 的問題。這篇文想來簡單記錄一下 CSP 是什麼和如何處理。

什麼是 CSP?

CSP 全名為 Content Security Policy ,它會藉由對頁面程式碼執行及資源載入的限制,一定程度內提升網站頁面的安全性。

最典型保護的就是腳本注入攻擊(XSS),譬如下方有一段 PHP 的程式碼:

<?php
// 資料庫帳號密碼等資訊...
// ...
$sql = "SELECT content FROM messages";
$result = $conn->query($sql);
// 這個 $messages 會存入 messages table 撈出的所有 messages
$messages = [];
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
array_push($messages, $row["content"]);
}
}
// ...
// 其他邏輯...
?>
<!DOCTYPE html>
<html>
<head>
<title>Messages</title>
</head>
<body>
<h1>Messages</h1>
<form method="POST" action="/messages/create_message.php">
<input type="text" name="message">
<button type="submit">Send</button>
</form>
<?php foreach($messages as $message): ?>
<ul>
<!-- 渲染資料 -->
<li><?php echo $message; ?></li>
</ul>
<?php endforeach; ?>
</body>

這是一個非常簡易且含有 XSS 漏洞的訊息功能,它可以呈現與新增訊息。其中在這邊使用了 echo

<li><?php echo $message; ?></li>

PHP 的 echo 會將傳入的字串作為 HTML 渲染出來,使用者在訊息中可以填入任何東西,例如 A normal message , <h1>Hello</h1> , 或是輸入

<script
src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
crossorigin="anonymous"></script>

由於訊息會在頁面中渲染,他就等於直接幫所有使用此頁面的使用者安裝了 jQuery,這當然只是一個無傷大雅的小玩笑。然而,倘若他輸入的 src 內容是他自行建立的惡意腳本,無辜的使用者們就會因此受害。

可定義 CSP 的方式——使用 meta

這時候,如果我們在 <head> 中新增一個 <meta> tag 並填入需要的內容,就可以讓瀏覽器去採取我們定義的 CSP:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src 'self'">

沒錯,就這麼單純而已!

這行 <meta> tag 的 content default-src ‘self’ 就是在告訴頁面說:「請你不要載入不同源的資源,包含 script, link 及 iframe」; img-src ‘self’ 的意思則是:「請你不要載入不同源的圖片」。如此一來,如果有心人士嘗試進行攻擊就會被阻擋:

因為違反頁面中定義的 CSP 政策,跨站腳本被拒絕載入

它也能防範直接在頁面內透過 <script> 執行 JavaScript:

因為違反頁面中定義的 CSP 政策,<script> 內直接寫的 JavaScript 被拒絕執行

如果使用者想要藉由 img 渲染不同網站的圖片也會被阻擋:

因為違反頁面中定義的 CSP 政策,圖片被拒絕載入

或是用 img 刻意存取不存在的圖片並透過 onerror 去執行 JavaScript 的行為:

因為違反頁面中定義的 CSP 政策,inline event handler 被拒絕執行

這樣設定的情況下,瀏覽器只會載入同源的 .css.js 資源:

在專案採用 Bundler 的狀況下,多數套件或框架最終都會被 Bundle 成一包或數包同源的資源在頁面中載入,如此就可以讓產品即使存在 XSS 的漏洞時,也能一定程度地防範產品使用者被有心人士攻擊。

但如果資源必須是在不同源的情況被載入,如 GA 或一些第三方的 SDK(可簡單理解為不同源的 .js 檔案),它們因為會有隨時更新的可能性,不允許我們將其下載下來直接存在我們自己的伺服器,這時候我們就可以針對這些比較特殊的第三方 SDK 設為例外的白名單:

<meta http-equiv="Content-Security-Policy" content="default-src 'self' cdnjs.cloudflare.com; img-src 'self' fakeimg.pl">

以這個例子來說,瀏覽器除了同源外,也會允許 cdnjs.cloudfare.com 的資源及 fakeimg.pl 的圖片:

<!DOCTYPE html>
<html>
<head>
<title>Messages</title>
<!-- CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self' cdnjs.cloudflare.com; img-src 'self' fakeimg.pl">
<link rel="stylesheet" type="text/css" href="/static/style.css">
</head>
<body>
<h1>Messages</h1>
<form method="POST" action="/messages/create_message.php">
<input type="text" name="message" />
<button type="submit">Send</button>
</form>
<?php foreach($messages as $message): ?>
<ul>
<li><?php echo $message; ?></li>
</ul>
<?php endforeach; ?>
<img src="https://fakeimg.pl/440x320/282828/eae0d0/?retina=1" alt="">
<img src="https://i.imgur.com/gLoK2Vw.jpeg" alt="">
<script src="https://www.not-friendly-site.tw/attack.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="/static/main.js"></script>

從上圖就可以看到,https://www.not-friendly-site.tw/attack.js 因為不符合頁面的 CSP 規範被阻擋、https://i.imgur.com/gLoK2Vw.jpeg 亦同。

另一個定義 CSP 的方式——寫在 Response header 中

CSP 除了 <meta> 外,也能定義在 Response header 中。以 PHP 為例:

header("Content-Security-Policy: default-src 'self' cdnjs.cloudflare.com; img-src 'self' fakeimg.pl");

在對頁面進行請求時,瀏覽器就會依據 Content-Security-Policy 的內容來遵循開發者定義的 CSP:

由於 Response Header 通常會由網頁伺服器來定義,如果專案的 CSP 不是寫在 <meta> tag 中,就要請後端或 SRE 幫忙調整設定了,例如我們的產品就是將 CSP 定義在 Response Header 中,後來是 CTO 大大協助改設定後就解決了載入不了某個第三方 SDK 的問題。

結語

CSP 可以一定程度地提升頁面的安全,進一步提升頁面被惡意攻擊的防禦力,但在開發上還是盡可能地預防讓有心人士攻擊的機會,不要把防禦的職責全部丟給瀏覽器我覺得才是最好的。

關於 block:csp 的狀況說明和解決方式就記錄到這,如果內文有任何錯誤也歡迎不吝指出,感謝你閱讀至此。

References:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP

--

--

Eason Lin
Eason Lin

Written by Eason Lin

Frontend Web Developer | Books

No responses yet