0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

#0209(2025/08/13)クリックジャッキング

0
Posted at

クリックジャッキングを根本から防ぐ実践ガイド

クリックジャッキングとは、ユーザーの意図しないクリック/タップを“見えないUI”や重ね合わせで誘導する攻撃である。


1. まずは全体像(なぜ危険か?)

  • 攻撃者サイトに透明(または極薄)な であなたのサイトを読み込み、本物のボタンを利用者に押させる。
  • 結果として、送金・購入・設定変更・SNSシェアなどが、ユーザーの気づかないうちに実行される。
  • 別名:UI Redressing。派生として Likejacking / Cursorjacking / Tapjacking(モバイル) など。

図:攻撃の流れ(概念図)

┌────────────┐     1. 誘導
│ 攻撃者のページ    │  ─────────▶ 「プレゼントを受け取る」等の餌UI
│ (evil.example)   │
├────────────┤     2. 重ね合わせ
│  透明iframe       │  ─────────▶ <iframe src="https://target.example/confirm" style="opacity:0.01;">
│(本物の画面)     │
└────────────┘     3. クリック
                            └──▶ ユーザーは餌をクリックしたつもりが本物ボタンを押下

2. 対策の基本方針(結論)

サーバ側ヘッダで “フレーム埋め込み禁止/制御” を行うのが第一。具体的には:

  1. CSP の frame-ancestors(推奨、柔軟で将来性あり)
  2. X-Frame-Options (XFO)(古参・広く効くが柔軟性に欠ける)
  3. (補助)クライアント側の フレーム検知+UI停止(回避困難にする追加ガード)

ベストプラクティス:CSP frame-ancestors を主、XFO を併用し、フレーム化検知でUXを停止


3. 主要対策の比較

対策 効果範囲 長所 注意点 推奨度
CSP: frame-ancestors サイトがどこから埋め込めるかを宣言 ドメイン単位で許可リストを柔軟指定、ポリシー一元管理 古いブラウザでは未対応の可能性(現行は概ねOK) ◎(主役)
X-Frame-Options (DENY/SAMEORIGIN) 全体/同一オリジンのみ許可 実装が簡単、長年の実績 ALLOW-FROMは実質非推奨・互換に難 ○(併用)
JSフレームバスティングif (top!==self) ... 埋め込まれた場合にUI停止 追加の心理的・操作的バリア JS無効やCSP・クリック透過など完全対策にはならない △(補助)
SameSite Cookie CSRFには有効 クリックジャッキング単体には効かない 混同注意 -(別目的)

4. 実装スニペット(サーバ設定)

4.1 推奨:CSP frame-ancestors

  • 全ブロック
Content-Security-Policy: frame-ancestors 'none';
  • 同一オリジンのみ
Content-Security-Policy: frame-ancestors 'self';
  • 特定パートナーのみ許可
Content-Security-Policy: frame-ancestors 'self' https://partner.example https://*.trusted.com;

重要:HTMLだけでなく、フレーム化され得るレスポンス(エラーページ含む)すべてに付与。

4.2 併用:X-Frame-Options

X-Frame-Options: DENY           # どこからもフレーム化不可
# or
X-Frame-Options: SAMEORIGIN     # 同一オリジンのみ可

4.3 各種サーバでの書き方

Nginx

add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header X-Frame-Options "SAMEORIGIN" always;

Apache (httpd.conf / .htaccess)

Header always set Content-Security-Policy "frame-ancestors 'self'"
Header always set X-Frame-Options "SAMEORIGIN"

Node.js (Express)

app.use((_, res, next) => {
  res.setHeader('Content-Security-Policy', "frame-ancestors 'self'");
  res.setHeader('X-Frame-Options', 'SAMEORIGIN');
  next();
});

Spring Security (Java)

http
  .headers(headers -> headers
    .contentSecurityPolicy(csp -> csp
      .policyDirectives("frame-ancestors 'self'"))
    .frameOptions(frame -> frame.sameOrigin())
  );

Go (net/http)

func secHeaders(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Security-Policy", "frame-ancestors 'self'")
    w.Header().Set("X-Frame-Options", "SAMEORIGIN")
    next.ServeHTTP(w, r)
  })
}

CDN/リバースプロキシ使用時は上書きに注意(例:CloudFront/NGINXで必ず always 指定)。


5. 許可付き埋め込みを安全に運用する

  • frame-ancestors許可ドメインを最小限に限定。
  • 子フレーム側でフレーム検知し、親のオリジン検証+**postMessageハンドシェイク**を実装。

ハンドシェイク例(簡略)

子(埋め込まれる側)

if (window.top !== window.self) {
  window.parent.postMessage({type:'hello', expected:'partner'}, '*');
  window.addEventListener('message', (e) => {
    if (new URL(e.origin).host.endsWith('partner.example')) {
      // OK: 続行
    } else {
      document.body.innerHTML = '<p>このページは許可されたサイトからのみ表示されます。</p>';
    }
  });
}

親(埋め込む側)

window.addEventListener('message', (e) => {
  if (e.data?.type === 'hello') {
    e.source.postMessage({type:'ack', from:location.origin}, e.origin);
  }
});

6. フロント側の“補助ガード”(フレーム検知)

意図せずフレーム表示されたら操作不可にする

<style>
  .is-framed * { pointer-events: none !important; }
  .is-framed::before { content: '安全のため操作をブロックしました';
    position: fixed; inset: 0; display: grid; place-items: center; background: rgba(0,0,0,.6); color: #fff; font: 600 16px/1.4 system-ui; }
</style>
<script>
  if (window.top !== window.self) {
    document.documentElement.classList.add('is-framed');
  }
</script>

これは補助的対策。ヘッダでのブロックが基本。


7. 誤解しやすい関連ヘッダの違い

ヘッダ/機能 目的 クリックジャッキング防止に直接効く?
CSP: frame-ancestors 埋め込み元の制御 はい(主役) 'none', 'self', 指定ドメイン
X-Frame-Options フレーム化可否 はい(補完) DENY, SAMEORIGIN
COOP/COEP/CORP 文書・リソースの分離/共有制御 いいえ(目的が違う) Spectre対策/共有制御
SameSite Cookie CSRF軽減 いいえ(別脅威) Lax/Strict

8. 自動テスト/監視に組み込む

  • ヘッダ検査
curl -sI https://your.example | sed -n 's/^Content-Security-Policy: .*$/&/p; s/^X-Frame-Options: .*$/&/p'
  • CIでの失敗条件

    • frame-ancestors が欠落 or 意図しないドメインが含まれる
    • 4xx/5xxエラーページにも付与されていない
  • ブラウザ検証:DevTools → Security / Network ヘッダ確認


9. 最小PoC(ローカルで挙動確認)

attacker.html(学習/検証用)

<!doctype html>
<body>
  <h1>無料ギフトを受け取る</h1>
  <button style="position:relative; z-index:1;">受け取る</button>
  <iframe src="https://target.example/confirm"
          style="position:absolute; top:0; left:0; width:100%; height:100%; opacity:0.01;">
  </iframe>
</body>

→ これが成立しないこと(=フレーム拒否/操作不可)を確認。


10. ありがちな落とし穴

  • 200だけに付けて404/500に未設定(エラーページが狙われる)。
  • CDN/ロードバランサでヘッダが消える/上書き
  • ALLOW-FROM を期待する(実質使えない)。
  • 一部機能だけ XFO: ALLOW を試みる(粒度が荒く危険)。
  • SPAで静的配信元API配信元で設定が分散し漏れる。

11. モバイルの補足(Tapjacking)

  • Androidではオーバーレイによるタップ乗っ取りが近縁の脅威。アプリ側で filterTouchesWhenObscured 等の対策が存在(Webとは別領域)。

12. 導入チェックリスト(30分でやる版)

  1. 方針決定:基本は frame-ancestors 'self'
  2. サーバ全経路に適用:200/301/302/404/500 を網羅。
  3. XFO SAMEORIGIN を併用(古いUA向け保険)。
  4. フロントのフレーム検知で操作停止(補助)。
  5. CIにヘッダ検査を追加、CDN/反映確認。

13. まとめ

  • クリックジャッキングはUI重ね合わせによる意図しない操作の強制。
  • CSP frame-ancestors を主軸に、XFO併用 + フレーム検知で多層防御。
  • 許可が必要な埋め込みは許可ドメイン最小化と**postMessageハンドシェイク**で安全に。

今日から実装するなら:frame-ancestors 'self' + X-Frame-Options: SAMEORIGIN を “常に” 返す。これが最小で強力。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?