[技術解析] 001 什麼是 X-Frame-Options 與 Content-Security-Policy (CSP) 中的 frame-ancestors

1. 為什麼需要防止網頁被惡意嵌入?

在網路世界中,常見一種名為「點擊劫持(Clickjacking)」的攻擊手法:
– 攻擊者建立一個惡意網站。
– 透過 <iframe><object> 暗中 載入你的網頁,把它藏在惡意網站的某處。
– 然後在網頁上疊加其它元素,誘導使用者 誤點擊

這樣一來,就可能造成:
– 帳號被盜用
– 信用卡等個資外洩
– 或其他安全相關的嚴重問題

所以,我們想做的事情很簡單:
1. 完全不允許 其他網站把我的網頁用框架(<iframe>)嵌入。
2. 或者只允許「特定且信任的來源」來嵌入。

常見的做法有兩個:
– 透過 X-Frame-Options(較早的規範),或
– 透過 CSP (Content-Security-Policy) 裡的 frame-ancestors


2. X-Frame-Options

2.1 這是什麼?

X-Frame-Options 是一個 HTTP 響應標頭。一旦瀏覽器接收到這個標頭,就會決定該網頁「能否被他人用框架 (<iframe>) 方式嵌入」。
– 主流瀏覽器大多支援 X-Frame-Options
– 不過設定值相對簡單,只有幾種可用選項。

2.2 常見設定

  1. DENY
    X-Frame-Options: DENY
    
    • 任何網頁都無法把你這個網頁放在 <iframe> 裡。
  2. SAMEORIGIN
    X-Frame-Options: SAMEORIGIN
    
    • 只有「同源(同協定 + 同網域 + 同埠號)」的網頁才能用框架嵌入。
  3. ALLOW-FROM
    X-Frame-Options: ALLOW-FROM https://partner.example.com
    
    • 只允許特定網域來嵌入。
    • 但是 Chrome 等瀏覽器對此的支援不佳。

2.3 實際專案中怎麼做?

2.3.1 Node.js/Express

const express = require('express');
const app = express();

// 設置 X-Frame-Options: DENY
app.use((req, res, next) => {
  res.setHeader('X-Frame-Options', 'DENY');
  next();
});

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
  • 只要有此設定,瀏覽器就會拒絕其他網頁使用 <iframe> 來載入此頁。

2.3.2 Apache

.htaccess 或 Apache 設定檔中加入:

<IfModule mod_headers.c>
    Header set X-Frame-Options "DENY"
</IfModule>
  • 設定完成後,重啟/重新載入 Apache 即可生效。

3. Content-Security-Policy (CSP) 的 frame-ancestors

3.1 這是什麼?

CSP 是一套更完整、更強大的安全機制,通常可以用來防禦像是「XSS」等攻擊。在它的一系列指令中,frame-ancestors 能做的事就是:
– 控制「哪些網域」可以使用 <iframe><object> 嵌入我這個頁面。

3.2 常見設定方式

  1. 全部禁止
    Content-Security-Policy: frame-ancestors 'none';
    
    • 任何人都不能用 <iframe> 載入你的頁面(類似 X-Frame-Options: DENY)。
  2. 只允許同源
    Content-Security-Policy: frame-ancestors 'self';
    
    • 只有「相同網域(含同協定、同埠號)」可以嵌入。
  3. 允許多個指定網域
    Content-Security-Policy: frame-ancestors 'self' https://example.com https://partner.example.com;
    
    • 同源 + example.com + partner.example.com 都可以嵌入。
  4. 允許任何 HTTPS 網域
    Content-Security-Policy: frame-ancestors https:;
    
    • 只要是透過 HTTPS 的網域都能嵌入(比較危險,不建議隨意使用)。

3.3 實際專案中怎麼做?

3.3.1 Node.js/Express

const express = require('express');
const app = express();

// 設定 CSP:只允許同源 + https://example.com
app.use((req, res, next) => {
  res.setHeader(
    'Content-Security-Policy',
    "frame-ancestors 'self' https://example.com"
  );
  next();
});

app.get('/', (req, res) => {
  res.send('Hello World! This page cannot be framed by unauthorized sites.');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
  • 'self' 代表同源
  • https://example.com 是另外指定可嵌入的網域

3.3.2 Nginx

server {
    listen 80;
    server_name mysite.com;

    location / {
        add_header Content-Security-Policy "frame-ancestors 'self' https://example.com" always;
        try_files uriuri/ =404;
    }
}
  • 重新載入 Nginx 後,非 'self'example.com 的網域想 <iframe> 載入 mysite.com,都會被擋下。

4. X-Frame-Options vs. frame-ancestors

4.1 可控性

  • X-Frame-Options 只能設定 DENYSAMEORIGINALLOW-FROM 三種。
  • frame-ancestors 不只可指定多個網域,還可以使用其它更複雜的條件,彈性更高

4.2 支援度

  • 舊版瀏覽器可能對 X-Frame-Options 的相容性較好。
  • ALLOW-FROM 一直以來在 Chrome 等瀏覽器支援度不佳。
  • 現代瀏覽器多建議使用 frame-ancestors

4.3 實務建議

  • 現在的最佳做法:以 CSP frame-ancestors 為主。
  • 若需要相容舊環境,則同時也加上 X-Frame-Options,提供「退回機制」。

5. 進階:同時使用 X-Frame-Options 與 CSP

範例(Node.js/Express):

const express = require('express');
const app = express();

// 同時設定 X-Frame-Options 與 CSP
app.use((req, res, next) => {
  res.setHeader('X-Frame-Options', 'SAMEORIGIN');
  // 設置完之後,frame-ancestors 仍然是主要依據
  res.setHeader('Content-Security-Policy', "frame-ancestors 'self' https://example.com");
  next();
});

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
  • 新版瀏覽器看到 Content-Security-Policy 會用 frame-ancestors 來判斷。
  • 舊版瀏覽器或不支援 CSP 的,則用 X-Frame-Options 來判斷。

6. 總結

  1. 點擊劫持(Clickjacking)透過 <iframe> 的暗中嵌入,可能導致嚴重的帳戶或資安問題。
  2. X-Frame-OptionsCSP frame-ancestors 都能控制「誰能使用 <iframe> 嵌入網頁」。
  3. X-Frame-Options 功能簡單、但相容度相對較早,frame-ancestors 則更彈性、更現代化。
  4. 建議:
    • 以 CSP(frame-ancestors)為主
    • 同時保留 X-Frame-Options 作為相容性備案。

只要善用這兩種機制,就能有效防範點擊劫持,保障網站與使用者的安全。

飛飛
飛飛