Webサイトを高速化するために導入したキャッシュが、攻撃者の「武器」に変わる——そんな攻撃が現実に起きています。
CDNやリバースプロキシのキャッシュは、Webパフォーマンスを劇的に改善します。しかし設定を誤ると、攻撃者が「悪意あるコンテンツ」をキャッシュに混入させ、それを何千人ものユーザーに一斉配信させてしまいます。これが「Webキャッシュポイズニング」です。
この記事では、Webキャッシュポイズニングの仕組みを攻撃者の視点から読み解き、CDN・リバースプロキシの設定で押さえるべき防御ポイントを現場で使えるレベルで解説します。
Webキャッシュポイズニングとは?
Webキャッシュポイズニング(Web Cache Poisoning)とは、攻撃者が意図的に「汚染されたHTTPレスポンス」をキャッシュサーバーに記憶させ、そのキャッシュを介してサイトを訪れる正規ユーザー全員に有害なコンテンツを配信させる攻撃です。
通常のXSSが「1人のユーザーを狙う」のに対し、Webキャッシュポイズニングは「キャッシュという仲介者を汚染して多数のユーザーに被害を拡大する」点が最大の特徴です。一度キャッシュへの混入が成功すれば、攻撃者が何もしなくてもキャッシュの有効期間(TTL)が切れるまで悪意あるコンテンツが配信され続けます。
影響を受けるシステムの代表例:
・グローバルCDN: Cloudflare・AWS CloudFront・Fastlyなど
・リバースプロキシ: nginx・Apache・Varnishをプロキシとして使うWebアーキテクチャ全般
・Webアプリキャッシュ: WordPressのキャッシュプラグイン(W3 Total Cache等)を含む
なお、DNSキャッシュポイズニングが「名前解決のキャッシュ」を汚染するのに対し、WebキャッシュポイズニングはHTTPレスポンスのキャッシュを標的にします。レイヤーが異なる別の攻撃です。
攻撃の仕組み——Cache Keyと「アンキー入力」を理解する
Webキャッシュポイズニングを理解するには、「Cache Key(キャッシュキー)」の概念が鍵になります。
1. Cache Keyとは何か
キャッシュサーバーは「同じリクエストには同じレスポンスを返す」ために、リクエストを識別するキーを使います。一般的にはURLとHTTPメソッド(GET/POST)がCache Keyとなります。
# 一般的なCache Keyのイメージ Cache-Key: GET https://example.com/index.html # 同じURLへのリクエストは全ユーザーに同一キャッシュを返す
2. アンキー入力(Unkeyed Input)の悪用
問題は、キャッシュキーには含まれないのにレスポンスに影響を与えるHTTPヘッダーが存在する点です。これを「アンキー入力(Unkeyed Input)」と呼びます。
たとえば `X-Forwarded-Host` や `X-Forwarded-Scheme` などのヘッダーは、多くの設定でCache Keyに含まれません。しかし、バックエンドアプリケーションがこれらの値を参照して動的なURLを生成することがあります。
# 攻撃者が送るリクエストのイメージ GET /index.html HTTP/1.1 Host: example.com X-Forwarded-Host: evil.attacker.com ← アンキー入力(Cache Keyに含まれない) # バックエンドがX-Forwarded-Hostを読み込んでURLを生成する場合、 # レスポンスにevil.attacker.comへの参照が混入する可能性がある
3. 汚染されたレスポンスがキャッシュに記録される
攻撃者が `X-Forwarded-Host: evil.attacker.com` を含むリクエストを送り、そのレスポンスがキャッシュされると次の連鎖が起きます。
・正規ユーザーは普通のURLにアクセスする
・キャッシュサーバーが汚染されたレスポンスを返す
・ユーザーのブラウザが攻撃者のドメインからスクリプトを読み込む
・セッションCookieの窃取や偽ページへの誘導が発生する
もっとも怖いのは「攻撃者が離席した後も、キャッシュのTTLが切れるまで攻撃が継続する」点です。
代表的な攻撃パターン
1. X-Forwarded-Host経由でのXSS混入
攻撃者が `X-Forwarded-Host` に自分のドメインを指定し、アプリケーションがそれをJavaScriptの読み込みURLとして反映する場合、キャッシュに汚染されたレスポンスが記録されます。その後アクセスした全ユーザーが悪意あるスクリプトを実行してしまいます。
XSS(クロスサイトスクリプティング)と同様のセッション窃取やフィッシング誘導が発生しますが、キャッシュが介在するため影響範囲がXSSより広くなりえます。
2. リダイレクト先の書き換え
HTTPからHTTPSへのリダイレクト処理で `X-Forwarded-Scheme: http` を読み込む設定がある場合、攻撃者が `X-Forwarded-Host: evil.com` を組み合わせると、レスポンスのリダイレクト先が攻撃者ドメインになります。それがキャッシュされると、サイト訪問者全員がフィッシングサイトへ転送されてしまいます。
3. Fat GETを悪用したポイズニング
一部のキャッシュサーバーは、GETリクエストのボディ(本文)をCache Keyに含めません。バックエンドがGETリクエストのボディを参照する設計の場合、「Cache Keyには影響しないがバックエンドのレスポンスには影響する」リクエストを細工できます。
また、SSRF(サーバーサイドリクエストフォージェリ)と組み合わせた攻撃チェーンも報告されており、単独の脆弱性にとどまらない複合的な脅威になりえます。
具体的な防御手順
1. Cache-Control ヘッダーを明示的に設定する
動的コンテンツには `Cache-Control: no-store` または `no-cache` を返すよう設定します。「すべてをキャッシュしない」ではなく、「キャッシュしてよいものだけを明示的にキャッシュする」が基本原則です。
# nginx設定例: 動的コンテンツのキャッシュ無効化 location /api/ { proxy_cache off; add_header Cache-Control "no-store, no-cache, must-revalidate"; } # 静的コンテンツのみキャッシュを許可(拡張子で判定) location ~* \.(css|js|png|jpg|gif|svg|woff2)$ { proxy_cache static_cache; proxy_cache_valid 200 24h; add_header Cache-Control "public, max-age=86400"; }
2. 不審なアンキー入力ヘッダーをプロキシ層で削除する
バックエンドに到達する前に、 `X-Forwarded-Host` など悪用されやすいヘッダーをリバースプロキシ層でフィルタリングします。自サービスで必要ないヘッダーは受け取り次第削除するのが安全策です。
# nginx設定例: クライアント起源のX-Forwarded-Hostを上書き・無効化 server { # 信頼できる値($host)で上書きし、外部からの入力を無視する proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-For $remote_addr; # 独自X-ヘッダーが不要なら明示的にクリアする proxy_set_header X-Custom-Header ""; }
リバースプロキシのセキュリティ設定全般については、別記事で詳しく解説しています。
3. CDNのキャッシュキーポリシーを明示的に定義する
CDNサービスでは「何をCache Keyに含めるか」を設定できます。不要なヘッダーがキャッシュキーの外に漏れ出してレスポンスに影響しないよう設定します。
主要CDNでの対応例:
・Cloudflare: Cache Rules と Transform Rules でヘッダーの除去・正規化が可能です
・AWS CloudFront: Cache Policy でキャッシュキーに含めるヘッダーを明示的に指定します。不要なヘッダーはオリジンリクエストポリシーでも制御します
・Fastly: VCL カスタマイズで `unset req.http.X-Forwarded-Host;` と記述し不要ヘッダーを削除できます
4. Vary ヘッダーの見直し
`Vary` ヘッダーは「このヘッダーの値が変われば別のキャッシュエントリを作る」という指示です。悪用されやすいヘッダーを `Vary` に指定することで、攻撃者のリクエストが既存キャッシュを上書きしにくくなります。ただし、 `Vary` に多くのヘッダーを指定するとキャッシュ効率が下がるため、必要最低限に絞ります。
中小企業・WordPressサイトでも今日からできること
「本格的なCDNやリバースプロキシは使っていない」という中小企業でも、今日から動ける対策があります。
・WordPressキャッシュプラグインの除外設定を確認する: W3 Total CacheやWP Super Cacheでは、ログイン済みユーザーのページ・WooCommerceのカートページ・会員限定コンテンツをキャッシュ対象から除外する設定が必ず必要です。設定漏れが情報漏洩につながります。
・管理画面(/wp-admin/)がキャッシュ対象外か確認する: 多くのキャッシュプラグインはデフォルトで除外していますが、明示的に設定を確認してください。
・Webホスティング会社への確認: 自動CDNを提供しているホスティングサービスの場合、「X-Forwarded-Hostヘッダーはエッジで除去されますか?」とサポートに問い合わせるだけでリスクの有無が判断できます。
・ブラウザの開発者ツールでレスポンスヘッダーを確認する: Cache-Control・Varyヘッダーが意図した値になっているかを定期的にチェックします。
WAF(Webアプリケーションファイアウォール)を導入している場合は、X-Forwardedヘッダーの異常値を検知するルールを有効にすることで、Webキャッシュポイズニングの試みを早期にブロックできます。
セキュリティヘッダー(CSP・HSTS・X-Frame-Options)の設定もあわせて見直すと、キャッシュポイズニングが成功した場合の被害を軽減できます。
よくある誤解と注意点
【誤解1】HTTPSさえ使えばキャッシュポイズニングは防げる
HTTPSはHTTP通信の暗号化であり、キャッシュサーバーへの汚染を防ぎません。暗号化された通信路でも、キャッシュに混入した悪意あるコンテンツはそのまま配信されます。
【誤解2】小規模サイトは狙われない
攻撃者は自動スキャンツールで脆弱なキャッシュ設定のサイトを無差別に探します。サイトの規模は無関係で、設定ミスがあれば標的になります。
【誤解3】キャッシュを完全に無効化すれば安全
キャッシュを全面停止するとパフォーマンスが大幅に低下し、サーバー負荷が急増します。「適切な範囲だけキャッシュする」設計が現実的な解決策です。
【注意】本番環境の設定変更は段階的に
nginxやApacheのキャッシュ設定変更は、必ずステージング環境で検証してから本番に適用してください。設定ミスによるサービス停止や意図しないキャッシュ漏洩が発生するリスクがあります。法律に関わる事項(不正アクセス禁止法・個人情報保護法等)は専門家にご確認ください。
本記事のまとめ
| 攻撃の視点 | 防御の対策 |
|---|---|
| Cache Keyに含まれないヘッダー(アンキー入力)を悪用 | X-Forwardedヘッダー等をプロキシ層で除去・上書きする |
| 汚染レスポンスがキャッシュに記録される | Cache-Controlヘッダーを明示的に設定する |
| TTL期間中すべてのユーザーに被害が拡大する | CDNのキャッシュキーポリシーを明示的に定義する |
| XSSやSSRFと連携した複合攻撃チェーンになる | WAFでX-Forwardedヘッダーの異常値を検知・ブロックする |
Webキャッシュポイズニングは、パフォーマンス最適化のために導入したキャッシュ機構が「攻撃の増幅装置」に変わるという逆説的な脅威です。「キャッシュを置いた覚えがある」なら、その設定を今日見直してみてください。
Linuxサーバーでnginxをリバースプロキシとして運用している場合のさらなる設定詳細は、姉妹サイトLinuxMaster.JPでも関連する情報を公開しています。
PR
体系的に学ぶ 安全なWebアプリケーションの作り方 第2版(徳丸浩/SBクリエイティブ)
キャッシュポイズニングを含むWebアプリ脆弱性の仕組みと対策を体系的に解説した定番書。XSS・SQLインジェクション・CSRFなどの実証コードと防御実装を丁寧に網羅しており、Webセキュリティを本格的に学びたいエンジニアの一冊目として最適です。
