使用內容安全策略 (CSP) 來控制哪些資源可以執行
若要控制延伸模組可以執行哪些內容,請在擴充功能的 manifest.json
檔案中,依照下列語法使用 content_security_policy
索引鍵及其原則字串值:
{
...,
"content_security_policy": "[policy string]"
...
}
例如,下列是默認內容安全策略,如下 列默認原則限制中所述:
{
...,
"content_security_policy": "script-src 'self'; object-src 'self'; worker-src 'self'"
...
}
為了減輕大型類別的潛在跨網站腳本問題,Microsoft Edge 延伸模組系統會納入內容安全策略 (CSP) 。 CSP 引進了一些嚴格的原則,讓擴充功能預設為更安全,並可讓您建立和強制執行規則,以控管可由延伸模組和應用程式載入及執行的內容類型。
一般而言,CSP 會作為擴充功能所載入或執行之資源的封鎖/允許清單機制。 為延伸模組定義合理的原則,可讓您仔細考慮擴充功能所需的資源,並要求瀏覽器確保這些資源是延伸模組唯一可以存取的資源。 這些原則可在擴充功能要求的主機許可權之上提供安全性;它們是額外的保護層,而不是取代。
相反地,在網頁中,這類原則是透過 HTTP 標頭或 meta
透過元素來定義。 但在Microsoft Edge 擴充功能系統內,HTTP 標頭或 meta
元素不是適當的機制。
請參閱:
- MDN 上的內容安全策略 (CSP) 。
- 指令清單 -Chrome 延伸模組參考中的內容安全策略>。
默認原則限制
未定義 manifest_version
的套件沒有預設內容安全策略。
使用 manifest_version
的套件具有下列預設內容安全策略:
此原則透過三種方式來限制延伸模組和應用程式,以增加安全性:
Eval 和相關函式已停用
如下所示的程式代碼無法運作:
alert(eval("foo.bar.baz"));
window.setTimeout("alert('hi')", 10);
window.setInterval("alert('hi')", 10);
new Function("return foo.bar.baz");
評估類似這樣的 JavaScript 字串是常見的 XSS 攻擊媒介。 相反地,您應該撰寫如下的程式代碼:
alert(foo && foo.bar && foo.bar.baz);
window.setTimeout(function() { alert('hi'); }, 10);
window.setInterval(function() { alert('hi'); }, 10);
function() { return foo && foo.bar && foo.bar.baz };
內嵌 JavaScript 未執行
不會執行內嵌 JavaScript 。 這項限制會同時禁止內嵌 <script>
區塊和內嵌事件處理程式,例如 <button onclick="...">
。
第一項限制會藉由讓您無法不小心執行惡意第三方所提供的腳本,來抹除大量的跨網站腳本攻擊類別。 不過,它確實會要求您撰寫程序代碼,並在內容和行為之間清楚分隔。 其中一個範例可能會讓這一點更清楚。 您可以嘗試將瀏覽器動作彈出視窗撰寫為單 pop-up.html
一,其中包含下列專案:
<!doctype html>
<html>
<head>
<title>My Awesome Pop-up!</title>
<script>
function awesome() {
// do something awesome!
}
function totallyAwesome() {
// do something TOTALLY awesome!
}
function clickHandler(element) {
setTimeout("awesome(); totallyAwesome()", 1000);
}
function main() {
// Initialization work goes here.
}
</script>
</head>
<body onload="main();">
<button onclick="clickHandler(this)">
Click for awesomeness!
</button>
</body>
</html>
但有三件事必須變更,才能讓此工作如預期的方式運作:
定義
clickHandler
必須移至外部 JavaScript 檔案 (popup.js
可能是良好的目標) 。內嵌事件處理程式定義必須以 來重寫,
addEventListener
並解壓縮到 中popup.js
。 如果您目前正在使用 之類的<body onload="main();">
程式代碼來啟動程式,請考慮根據您的需求,藉由連結到DOMContentLoaded
檔的事件或load
視窗的事件來取代程式。 使用前者,因為它通常會更快速地觸發。setTimeout
必須重寫呼叫,以避免將字串"awesome(); totallyAwesome()"
轉換成 JavaScript 以執行。
這些變更看起來可能如下所示:
function awesome() {
// Do something awesome!
}
function totallyAwesome() {
// do something TOTALLY awesome!
}
function awesomeTask() {
awesome();
totallyAwesome();
}
function clickHandler(e) {
setTimeout(awesomeTask, 1000);
}
function main() {
// Initialization work goes here.
}
// Add event listeners once the DOM has fully loaded by listening for the
// `DOMContentLoaded` event on the document, and adding your listeners to
// specific elements when it triggers.
document.addEventListener('DOMContentLoaded', function () {
document.querySelector('button').addEventListener('click', clickHandler);
main();
});
<!doctype html>
<html>
<head>
<title>My Awesome Pop-up!</title>
<script src="popup.js"></script>
</head>
<body>
<button>Click for awesomeness!</button>
</body>
</html>
只會載入本機腳本和對象資源
腳本和對象資源只能從擴充套件載入,不能從大型網頁載入。 這可確保您的擴充功能只會執行您特別核准的程序代碼,防止作用中的網路攻擊者惡意地重新導向資源的要求。
請考慮在延伸模組套件中包含特定版本的 jQuery,而不是撰寫相依於 jQuery (或任何其他連結庫) 從外部 CDN 載入的程式代碼。 也就是說,而不是:
<!doctype html>
<html>
<head>
<title>My Awesome Pop-up!</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
</head>
<body>
<button>Click for awesomeness!</button>
</body>
</html>
請改用下列方法。 下載檔案、將它包含在您的套件中,然後寫入:
<!doctype html>
<html>
<head>
<title>My Awesome Pop-up!</title>
<script src="jquery.min.js"></script>
</head>
<body>
<button>Click for awesomeness!</button>
</body>
</html>
放寬默認原則
您可以允許執行下列型態的文稿:
詳細數據如下。
內嵌腳本
在原則中指定原始程式碼的base64編碼哈希,即可允許內嵌腳本。 這個哈希前面必須加上使用的哈希演算法, (sha256、sha384 或 sha512) 。 如需範例,請參閱腳本>專案的 W3C > 哈希使用方式<。
遠端文本
如果您需要一些外部 JavaScript 或物件資源,您可以藉由允許列出應接受腳本的安全來源,將原則放寬至有限的範圍。 確認以提升的擴充功能許可權載入的運行時間資源完全是您預期的資源,而且不會由作用中的網路攻擊者取代。 由於 攔截式攻擊 是透過 HTTP 進行簡單且無法偵測到的攻擊,因此不接受這些來源。
目前,您可以允許具有下列配置的清單來源: blob
、 filesystem
、 https
和 extension
。 必須針對和 extension
配置明確指定https
來源的主機部分。 不允許一般通配符,例如 HTTPs:, https://*
和 https://*.com
;允許子域通配符,例如 https://*.example.com
。
公用後綴清單中的網域也會視為一般最上層網域。 若要從這些網域載入資源,必須明確列出子域。 例如, https://*.cloudfront.net
不合法,但 https://XXXX.cloudfront.net
與 https://*.XXXX.cloudfront.net
可以是 allowlisted
。
為了方便開發,從本機電腦上的伺服器透過 HTTP 載入的資源可以是 allowlisted
。 您可以在或 http://localhost
的任何連接埠http://127.0.0.1
上允許列出文稿和物件來源。
注意事項
透過 HTTP 載入的資源限制僅適用於直接執行的資源。 例如,您仍然可以自由連線到 XMLHTTPRequest
任何您喜歡的來源;默認原則不會以任何方式限制 connect-src
或任何其他 CSP 指示詞。
允許從 example.com
HTTPS 載入文稿資源的寬鬆原則定義可能如下所示:
"content_security_policy": "script-src 'self' https://example.com; object-src 'self'"
注意事項
和 object-src
都是script-src
由原則所定義。 Microsoft Edge 不接受不會將每個值限制為至少) 'self
' (的原則。
評估的 JavaScript
您可以藉由將、 和 等setTimeout(String)
相關函式新unsafe-eval
增至原則來放寬原則eval()
: setInterval(String)
new Function(String)
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
不過,您應該避免放寬原則。 這些類型的函式是一系列的 XSS 攻擊媒介。
強化默認原則
您可以將此原則強化到延伸模組允許的任何範圍,以增加安全性,但代價是方便。 若要指定您的擴充功能只能從相關聯的擴充套件載入任何類型的資源 (映射等) ,例如, default-src 'self'
的原則可能很適當。
內容腳本
正在討論的原則適用於擴充功能的背景頁面和事件頁面。 內容腳本如何套用至擴充功能的內容腳本更為複雜。
內容腳本通常不受限於擴充功能的 CSP。 因為內容腳本不是 HTML,所以其主要影響在於即使擴充功能的 CSP 未指定 unsafe-eval
,仍可使用eval
這些腳本,但不建議這麼做。 此外,頁面的 CSP 不適用於內容腳本。 更複雜的是 <script>
內容腳本所建立並放入其執行頁面之 DOM 的標籤。 這些會參考為 DOM 插入的腳本。
插入頁面時立即執行的 DOM 插入文本會如預期般執行。 假設內容文稿具有下列程式代碼作為簡單範例:
document.write("<script>alert(1);</script>");
此內容文稿會立即在 上document.write()
產生 alert
。 請注意,無論頁面指定的原則為何,都會執行此動作。 不過,在該 DOM 插入的腳本內,以及任何不會在插入時立即執行的腳本,行為都會變得更複雜。
假設您的擴充功能正在提供指定 之相關聯 CSP script-src 'self'
的頁面上執行。 現在假設內容文稿會執行下列程式代碼:
document.write("<button onclick='alert(1);'>click me</button>'");
如果使用者按下該按鈕, onclick
則不會執行腳本。 這是因為腳本不會立即執行,而且在事件發生之前 click
不會解譯的程式代碼不會被視為內容腳本的一部分,因此頁面的 CSP (不是擴充功能,) 限制行為。 而且,因為該 CSP 未指定 unsafe-inline
,所以會封鎖內嵌事件處理程式。
在此情況下,實作所需行為的正確方式是從內容腳本將處理程式新增 onclick
為函式,如下所示:
document.write("<button id='mybutton'>click me</button>'");
var button = document.getElementById('mybutton');
button.onclick = function() {
alert(1);
};
如果內容文稿執行下列專案,就會發生另一個類似的問題:
var script = document.createElement('script');
script.innerHTML = 'alert(1);'
document.getElementById('body').appendChild(script);
在此情況下,腳本會執行,並顯示警示。 不過,請考慮此情況:
var script = document.createElement('script');
script.innerHTML = 'eval("alert(1);")';
=document.getElementById('body').appendChild(script);
當初始腳本執行時,會封鎖 對 eval
的呼叫。 也就是說,雖然允許初始腳本運行時間,但腳本內的行為會受到頁面的 CSP 規範。 因此,視您在延伸模組中撰寫 DOM 插入腳本的方式而定,頁面的 CSP 變更可能會影響延伸模塊的行為。
因為內容腳本不會受到頁面的 CSP 影響,所以這是盡可能將延伸模塊的行為放入內容腳本,而不是 DOM 插入腳本的絕佳理由。
另請參閱
- MDN 上的內容安全策略 (CSP) 。
- 指令清單 -Chrome 延伸模組參考中的內容安全策略>。
注意事項
此頁面的部分是根據Google所建立和 共用的工作進行 修改,並根據 Creative Commons Attribution 4.0 國際授權中所述的條款使用。 您可以 在這裡找到原始頁面。
此工作是根據 Creative Commons Attribution 4.0 International License 授權。