跳至主要内容

beyond CORS (上篇)

行前準備

本篇文章,會大量用到 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 NameHeader Type
Cross-Origin-Resource-PolicyResponse
Cross-Origin-Embedder-PolicyResponse
Cross-Origin-Opener-PolicyResponse

Cross-Origin-Resource-Policy (CORP)

誕生原因

CORP 的誕生,是為了解決 MeltdownSpectre 這些資安漏洞,詳細可參考 Huli 大大的文章,我沒有深入去瞭解這些資安漏洞的原因跟 PoC,但可以用一個簡單的時序圖,帶大家了解

一石二鳥

  • CORP 的誕生,也順便解決了另一個問題,就是保護自家網站的資源不被跨域的網站引用
  • 這邊的資源,通常是指透過 <img> 或是 <script> 載入的資源
  • 如果是 HTML 類型的資源,不想要被跨域的網站透過 <iframe> 載入,則無法透過 CORP 防範,請參考我寫過的 iframe-security

CORP 實作環節

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-html-tags-test

CORP 的 error message corp-error-message

img, link, script, video, audio 的 response body 都無法讀取 corp-no-response-body

即便有設定 CORP,iframe 還是可以正常載入,X-Frame-Options, CSP frame-ancestors 才可以防止 iframe 的 response body 被載入 x-frame-options-no-response-body

iframe vs CORP

Response Header能否防止 iframe 載入跨域的 response body說明
Cross-Origin-Resource-Policyiframe 不算是 resource,而是一個 browsing context,所以不受 CORP 影響
X-Frame-Options

browser MAY immediately abort downloading or parsing of the document x-frame-options-discard-body

CSP frame-ancestors

CSP 的規範比較嚴格,是用 MUST 來描述 csp-frame-ancestors-discard-body

Cross-Origin-Embedder-Policy (COEP)

COEP vs CORP

如果以買水果的概念來比喻的話

  • COEP 的概念是 => 我只買沒有農藥的水果
  • CORP 的概念是 => 商家的水果決定要賣給誰
  • 雙方都要同意才會成交

我把 COEP 跟 CORP 的概念以及交乘情境都列出來,方便大家用圖表的方式去理解

P.S. Case 3-3 Browser ignore the response body because of COEP 的情境,會看到以下錯誤訊息 coep-block-corp-not-set

小結

CORP 跟 COEP,實務上我沒看到有網站使用過,但它們也是瀏覽器安全性演進的一環。另外還有 COOP,會在 未來的章節 跟大家分享~

參考資料