#はじめに
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-age | HTTPSを強制する期間(秒)。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-origin | HTTPSからHTTPへは送信しない、オリジンのみ |
strict-origin-when-cross-origin | 推奨。上記の組み合わせ |
no-referrer-when-downgrade | HTTPSから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で確認
- DevToolsのNetworkタブを開く
- ドキュメント(HTMLファイル)のリクエストを選択
- Headersタブでレスポンスヘッダーを確認
#オンラインツール
- Security Headers - ヘッダーのスキャンとグレード付け
- Mozilla Observatory - 総合的なセキュリティチェック
#実装例
#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について学びましょう。これは比較的新しい仕様で、サーバー側でリクエストのコンテキストを判断し、不正なリクエストを拒否するのに役立ちます。