すべてのウェブサイトが送るべきセキュリティヘッダー
サーバーを堅牢化し、フレームワークにパッチを当て、チームにセキュアコーディングを教育したとしても、適切なHTTPセキュリティヘッダーを送信していなければ、サイトは依然として脆弱なままです。これらのレスポンスヘッダーは、コンテンツを処理する際のブラウザの動作を指示するものであり、ヘッダーが欠けていたり誤設定されていると、クロスサイトスクリプティング(XSS)、クリックジャッキング、プロトコルダウングレード攻撃、データ漏洩への扉を開いたままにすることになります。
このガイドでは、すべてのウェブサイトが送るべき必須セキュリティヘッダーを取り上げ、それぞれの役割を説明し、正しい設定方法を紹介します。
セキュリティヘッダーが重要な理由
セキュリティヘッダーは、あなたが制御できるブラウザレベルのポリシーを強制するため、最初の防御線となります。入力検証や安全な認証の代わりにはなりませんが、攻撃対象領域を大幅に減らすことができます。たとえば、厳格なContent-Security-Policyは、攻撃者がXSSの脆弱性を見つけたとしても、注入されたスクリプトの実行を阻止できます。
主要なブラウザはこれらのヘッダーを一貫してサポートしており、追加には通常数行の設定しかかかりません。使わない理由はほとんどありません。
必須のセキュリティヘッダー
以下は、すべての本番ウェブサイトが送るべきヘッダーです。それぞれの役割、推奨値、よくある落とし穴を説明します。
1. Content-Security-Policy(CSP)
CSPは、XSSとデータインジェクションを軽減するための最も強力なヘッダーです。ブラウザがスクリプト、スタイル、画像などのリソースを読み込めるソースを制限します。適切な初期ポリシーの例は次のとおりです:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://images.example.com; object-src 'none'; base-uri 'self'; form-action 'self';default-src 'self'から始め、必要に応じて例外を徐々に追加していきます。可能であればスクリプトに'unsafe-inline'を使うのは避け、代わりにnonceやハッシュを使用してください。CSPの誤設定はサイトを壊す可能性があるため、十分にテストしましょう。
2. HTTP Strict Transport Security(HSTS)
HSTSは、あなたのドメインへの将来のすべてのリクエストでHTTPSの使用をブラウザに強制します。プロトコルダウングレード攻撃やCookieハイジャックを防ぎます。典型的なヘッダーは次のとおりです:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadmax-ageは秒単位です(1年)。includeSubDomainsはすべてのサブドメインにポリシーを適用します。preloadを使うと、あなたのドメインをブラウザのプリロードリストに追加できますが、すべてのサブドメインがHTTPSをサポートしていると確信できる場合にのみ使用してください。
3. X-Frame-Options
このヘッダーは、あなたのサイトがiframeに埋め込まれるのを防ぎ、クリックジャッキング攻撃を阻止します。次のように使用します:
X-Frame-Options: DENY自分のコンテンツをフレーム内に表示する必要がある場合はSAMEORIGINを使用します。最新のブラウザは、より柔軟なCSPのframe-ancestorsディレクティブもサポートしています。CSPを使用する場合はX-Frame-Optionsを省略できますが、両方を含めることで互換性が向上します。
4. X-Content-Type-Options
このヘッダーは、ブラウザが宣言されたContent-Typeとは異なるMIMEスニッフィングを行うのを防ぎます。シンプルで効果的です:
X-Content-Type-Options: nosniffこれがないと、悪意のあるファイルが実行可能なスクリプトとして解釈される可能性があります。必ず設定しましょう。
5. Referrer-Policy
Referrer-Policyは、リクエストとともに送信されるリファラー情報の量を制御します。バランスの取れたデフォルトは次のとおりです:
Referrer-Policy: strict-origin-when-cross-originこれは、同一オリジンのリクエストには完全なURLを送信し、クロスオリジンのリクエストにはオリジンのみを送信します。アナリティクスを維持しつつ、機密性の高いパス情報の漏洩を減らします。
6. Permissions-Policy
以前はFeature-Policyと呼ばれていたこのヘッダーは、位置情報、カメラ、マイクなどのブラウザ機能を有効または無効にできます。例:
Permissions-Policy: geolocation=(), camera=(), microphone=()未使用の機能を無効にすることで、侵害されたサードパーティスクリプトの影響を減らせます。
セキュリティヘッダーの比較
| ヘッダー | 目的 | 推奨値 |
|---|---|---|
| Content-Security-Policy | XSSとデータインジェクションの軽減 | default-src 'self'; script-src 'self' ... |
| Strict-Transport-Security | HTTPSの強制 | max-age=31536000; includeSubDomains |
| X-Frame-Options | クリックジャッキングの防止 | DENYまたはSAMEORIGIN |
| X-Content-Type-Options | MIMEスニッフィングの停止 | nosniff |
| Referrer-Policy | リファラー漏洩の制御 | strict-origin-when-cross-origin |
| Permissions-Policy | ブラウザ機能の制限 | geolocation=(), camera=() |
セキュリティヘッダーの追加方法
方法はウェブサーバーやフレームワークによって異なります。以下は一般的なアプローチです。
Nginx
サーバーブロックにヘッダーを追加します:
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://images.example.com; object-src 'none'; base-uri 'self'; form-action 'self';" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;alwaysパラメータは、エラーレスポンスでもヘッダーが送信されることを保証します。
Apache
mod_headersを有効にして次を追加します:
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://images.example.com; object-src 'none'; base-uri 'self'; form-action 'self';"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Frame-Options "DENY"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), camera=(), microphone=()"Node.js(Express)
多くのヘッダーをデフォルトで設定するhelmetミドルウェアを使用します:
const helmet = require('helmet');
app.use(helmet());
// 必要に応じてCSPをカスタマイズ
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://trusted.cdn.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https://images.example.com"],
objectSrc: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"],
}
}));HelmetはX-Content-Type-OptionsやReferrer-Policyなど、他のヘッダーもデフォルトで設定します。
ヘッダーのテスト
デプロイ後、ブラウザの開発者ツール(Networkタブ)やSecurityHeaders.comなどのオンラインスキャナーを使ってヘッダーを検証しましょう。以下を確認してください:
- 推奨されるすべてのヘッダーが存在する。
- CSPが正当なリソースをブロックしていない(コンソールで違反を確認)。
- HTTPSが完全に機能している場合にのみHSTSが有効になっている。
- エラーページでもヘッダーが送信されている。
サイトの進化に合わせて、定期的にポリシーを見直し、更新しましょう。
FAQ
最も重要なセキュリティヘッダーは何ですか?
Content-Security-Policyは、最も一般的なウェブ脆弱性の一つであるXSSとデータインジェクション攻撃を直接軽減するため、最も重要と考えられることが多いです。
セキュリティヘッダーは他のセキュリティ対策を置き換えられますか?
いいえ。セキュリティヘッダーは多層防御の一層です。安全なコーディング、入力検証、認証、その他のベストプラクティスは依然として必要です。
セキュリティヘッダーを追加するとウェブサイトが壊れますか?
誤設定されている場合、特にCSPは正当なリソースをブロックする可能性があります。本番環境にデプロイする前に、必ずステージング環境でテストし、ブラウザコンソールで違反を監視してください。
サイトを堅牢化する準備はできましたか?まず上記のヘッダーを追加し、ブラウザの開発者ツールでテストしましょう。セキュリティスキャナーからのJSONレスポンスをすばやく検査・整形するには、JSON Formatterをお試しください。