#はじめに

CSP以外にも、Webアプリケーションを保護するためのHTTPヘッダーがあります。これらを適切に設定することで、クリックジャッキング、MIMEスニッフィング、中間者攻撃など、様々な脅威からユーザーを守ることができます。

この記事を読むと、以下のことができるようになります:

  • 主要なセキュリティヘッダーの役割を理解できる
  • 各ヘッダーを適切に設定できる
  • 自分のサイトのセキュリティヘッダーを確認・改善できる

#X-Frame-Options

X-Frame-Optionsは、ページが<iframe>内に埋め込まれることを制御するヘッダーです。

#クリックジャッキング攻撃

<!-- 攻撃者のサイト evil.com -->
<style>
  iframe { opacity: 0; position: absolute; top: 0; left: 0; }
  .fake-button { position: relative; z-index: -1; }
</style>

<iframe src="https://bank.com/transfer?to=attacker&amount=10000"></iframe>
<button class="fake-button">今すぐ無料ダウンロード!</button>

ユーザーは「無料ダウンロード」ボタンをクリックしたつもりが、実際には透明なiframe内の「送金」ボタンをクリックしてしまいます。

#設定値

# iframeへの埋め込みを完全に禁止
X-Frame-Options: DENY

# 同一オリジンからの埋め込みのみ許可
X-Frame-Options: SAMEORIGIN

#CSPとの関係

CSPのframe-ancestorsディレクティブが後継です。

Content-Security-Policy: frame-ancestors 'self'

両方設定する場合、CSPが優先されます。後方互換性のため、両方設定することが推奨されます。

#X-Content-Type-Options

X-Content-Type-Optionsは、ブラウザによるMIMEタイプのスニッフィングを防止するヘッダーです。

#MIMEスニッフィング攻撃

# サーバーが返すContent-Type
Content-Type: text/plain

# しかし、ファイルの中身がJavaScriptっぽい場合...
<script>alert('XSS')</script>

# ブラウザがスニッフィングして、JavaScriptとして実行してしまう

#設定

X-Content-Type-Options: nosniff

これにより、ブラウザはContent-Typeヘッダーを厳格に尊重し、スニッフィングを行いません。

#効果

  • スクリプトやスタイルシートは、正しいMIMEタイプが設定されていないと読み込まれない
  • 意図しないコンテンツの実行を防止

#Strict-Transport-Security(HSTS)

HSTSは、ブラウザに「このサイトにはHTTPSでのみアクセスする」と記憶させるヘッダーです。

#HTTPからHTTPSへのリダイレクトの問題

1. ユーザーが http://example.com にアクセス
2. サーバーが https://example.com にリダイレクト
3. ユーザーがHTTPSで通信

問題: 1のHTTPリクエストが攻撃者に傍受される可能性

HSTSがあれば、2回目以降のアクセスは最初からHTTPSになります。

#設定

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
パラメータ説明
max-ageHTTPSを強制する期間(秒)。1年 = 31536000
includeSubDomainsサブドメインにも適用
preloadブラウザのプリロードリストに登録を希望

#プリロードリスト

ブラウザには、最初からHTTPSが必須なサイトのリストがあります。登録することで、初回アクセスからHTTPSが強制されます。

登録サイト: https://hstspreload.org/

#注意点

  • HTTPSが正常に動作することを確認してから設定
  • max-ageを短く設定して始め、徐々に延ばす
  • HTTPS化が不完全だと、サイトにアクセスできなくなる

#X-XSS-Protection(非推奨)

X-XSS-Protection: 1; mode=block

かつてはXSSフィルターを有効化するために使用されましたが、現在は非推奨です。

#非推奨の理由

  • ブラウザのXSSフィルターが廃止された(Chrome、Edge)
  • 誤検知による問題が発生することがある
  • CSPがより効果的な代替手段

#推奨設定

現在は無効化が推奨されます:

X-XSS-Protection: 0

#Referrer-Policy

Referrer-Policyは、リクエストに含まれるRefererヘッダーの情報量を制御します。

#Refererヘッダーの問題

# ユーザーが https://example.com/secret-page からリンクをクリック
# 遷移先に送信されるRefererヘッダー:
Referer: https://example.com/secret-page

# URLにセンシティブな情報が含まれていると...
Referer: https://example.com/reset-password?token=abc123
# トークンが漏洩!

#設定値

Refererの内容
no-referrer送信しない
originオリジンのみ(https://example.com
origin-when-cross-origin同一オリジン: フルURL、クロスオリジン: オリジンのみ
same-origin同一オリジンのみ送信
strict-originHTTPSからHTTPへは送信しない、オリジンのみ
strict-origin-when-cross-origin推奨。上記の組み合わせ
no-referrer-when-downgradeHTTPSからHTTPへは送信しない

#推奨設定

Referrer-Policy: strict-origin-when-cross-origin

#Permissions-Policy

Permissions-Policyは、ブラウザの機能(カメラ、マイク、位置情報など)の使用を制限するヘッダーです。

旧名称: Feature-Policy

#設定例

Permissions-Policy: camera=(), microphone=(), geolocation=(self)
設定意味
camera=()カメラを完全に無効化
microphone=()マイクを完全に無効化
geolocation=(self)位置情報は自分自身のみ許可
payment=(self "https://payment.example.com")決済は自分と特定のオリジンのみ

#制御できる機能

  • camera, microphone(カメラ・マイク)
  • geolocation(位置情報)
  • payment(Payment Request API)
  • fullscreen(全画面表示)
  • autoplay(自動再生)
  • など多数

#推奨するセキュリティヘッダー一覧

# クリックジャッキング対策
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'

# MIMEスニッフィング対策
X-Content-Type-Options: nosniff

# HTTPS強制
Strict-Transport-Security: max-age=31536000; includeSubDomains

# リファラー制御
Referrer-Policy: strict-origin-when-cross-origin

# XSSフィルター無効化(廃止されたため)
X-XSS-Protection: 0

# 機能制限
Permissions-Policy: camera=(), microphone=(), geolocation=()

#セキュリティヘッダーの確認

#DevToolsで確認

  1. DevToolsのNetworkタブを開く
  2. ドキュメント(HTMLファイル)のリクエストを選択
  3. Headersタブでレスポンスヘッダーを確認

#オンラインツール

#実装例

#nginx

add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-XSS-Protection "0" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

#Express.js(helmet使用)

const helmet = require('helmet');
app.use(helmet());

// または個別に設定
app.use(helmet.frameguard({ action: 'deny' }));
app.use(helmet.noSniff());
app.use(helmet.hsts({ maxAge: 31536000, includeSubDomains: true }));
app.use(helmet.referrerPolicy({ policy: 'strict-origin-when-cross-origin' }));

#まとめ

  • X-Frame-Options: クリックジャッキング対策(CSPのframe-ancestorsと併用)
  • X-Content-Type-Options: nosniff: MIMEスニッフィング防止
  • Strict-Transport-Security: HTTPS強制
  • Referrer-Policy: リファラー情報の制御
  • X-XSS-Protection: 0: 廃止された機能を明示的に無効化
  • Permissions-Policy: ブラウザ機能の制限

#次のステップ

主要なセキュリティヘッダーを理解したところで、次はFetch Metadata Headersについて学びましょう。これは比較的新しい仕様で、サーバー側でリクエストのコンテキストを判断し、不正なリクエストを拒否するのに役立ちます。

#参考リンク