本篇文章,會大量用到 NodeJS HTTP Server 作為程式碼範例,為了避免重複,所以這邊先把基礎架構設定好
httpServers.ts
import { createServer } from "http";
export const http5000Server = createServer().listen(5000);
export const http5001Server = createServer().listen(5001);
index.ts
import { faviconListener } from "../listeners/faviconListener";
import { notFoundListener } from "../listeners/notFoundlistener";
import { http5000Server, http5001Server } from "./httpServers";
http5000Server.on("request", function requestListener(req, res) {});
http5001Server.on("request", function requestListener(req, res) {});
底下網羅本篇文章會介紹到的 "beyond CORS" Headers,會在接下來的段落陸續介紹到
Header Name | Header Type |
---|---|
Cross-Origin-Resource-Policy | Response |
Cross-Origin-Embedder-Policy | Response |
Cross-Origin-Opener-Policy | Response |
CORP 的誕生,是為了解決 Meltdown
跟 Spectre
這些資安漏洞,詳細可參考 Huli 大大的文章,我沒有深入去瞭解這些資安漏洞的原因跟 PoC,但可以用一個簡單的時序圖,帶大家了解
<img>
或是 <script>
載入的資源<iframe>
載入,則無法透過 CORP 防範,請參考我寫過的 iframe-security
http5000Server
const indexHTML = readFileSync(join(__dirname, "index.html"));
if (req.url === "/") {
res.setHeader("Content-Type", "text/html");
res.end(indexHTML);
return;
}
index.html
<html>
<head>
<link rel="stylesheet" href="http://localhost:5001/style.css" />
</head>
<body>
<img src="http://localhost:5001/image.png" />
<script src="http://localhost:5001/script.js"></script>
<video src="http://localhost:5001/video.mp4"></video>
<audio src="http://localhost:5001/audio.aac"></audio>
<iframe src="http://localhost:5001/with-corp.html"></iframe>
<iframe src="http://localhost:5001/with-x-frame-options.html"></iframe>
<iframe src="http://localhost:5001/with-csp-frame-ancestors.html"></iframe>
</body>
</html>
http5001Server
if (req.url === "/style.css") {
res.setHeader("Cross-Origin-Resource-Policy", "same-origin");
res.setHeader("Content-Type", "text/css");
res.end("123");
return;
}
if (req.url === "/image.png") {
res.setHeader("Cross-Origin-Resource-Policy", "same-origin");
res.setHeader("Content-Type", "image/png");
res.end("123");
return;
}
if (req.url === "/script.js") {
res.setHeader("Cross-Origin-Resource-Policy", "same-origin");
res.setHeader("Content-Type", "text/javascript");
res.end("123");
return;
}
if (req.url === "/video.mp4") {
res.setHeader("Cross-Origin-Resource-Policy", "same-origin");
res.setHeader("Content-Type", "video/mp4");
res.end("123");
return;
}
if (req.url === "/audio.aac") {
res.setHeader("Cross-Origin-Resource-Policy", "same-origin");
res.setHeader("Content-Type", "audio/aac");
res.end("123");
return;
}
if (req.url === "/with-corp.html") {
res.setHeader("Cross-Origin-Resource-Policy", "same-origin");
res.setHeader("Content-Type", "text/html");
res.end("123");
return;
}
if (req.url === "/with-x-frame-options.html") {
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("Content-Type", "text/html");
res.end("123");
return;
}
if (req.url === "/with-csp-frame-ancestors.html") {
res.setHeader("Content-Security-Policy", "frame-ancestors 'self'");
res.setHeader("Content-Type", "text/html");
res.end("123");
return;
}
瀏覽器打開 http://localhost:5000/ ,可看到 F12 > Network 如下
CORP 的 error message
img, link, script, video, audio 的 response body 都無法讀取
即便有設定 CORP,iframe 還是可以正常載入
X-Frame-Options, CSP frame-ancestors 才可以防止 iframe 的 response body 被載入
Response Header | 能否防止 iframe 載入跨域的 response body | 說明 |
---|---|---|
Cross-Origin-Resource-Policy | ❌ | iframe 不算是 resource,而是一個 browsing context,所以不受 CORP 影響 |
X-Frame-Options | ✅ | browser MAY immediately abort downloading or parsing of the document![]() |
CSP frame-ancestors | ✅ | CSP 的規範比較嚴格,是用 MUST 來描述![]() |
如果以買水果的概念來比喻的話
我把 COEP 跟 CORP 的概念以及交乘情境都列出來,方便大家用圖表的方式去理解
P.S. Case 3-3 Browser ignore the response body because of COEP 的情境,會看到以下錯誤訊息
CORP 跟 COEP,實務上我沒看到有網站使用過,但它們也是瀏覽器安全性演進的一環。另外還有 COOP,會在 未來的章節 跟大家分享~