Webキャッシュポイズニングは、キャッシュに汚染したレスポンスを保存させ、CDN経由で多数の利用者に配信させる脆弱性です。 対策の勘所は、キャッシュのキーを正しく設計し、キーに含まれない入力を応答に混ぜないことです。
Webキャッシュポイズニングとは
Webキャッシュポイズニング(Webキャッシュ汚染):CDNやリバースプロキシのキャッシュに、攻撃者が細工した有害なレスポンスを保存させ、同じキャッシュキーを参照する他の利用者へ配信させる攻撃です。 キャッシュのキーに含まれない入力が、レスポンスの内容に影響することが根本原因になります。
キャッシュは同じキー(多くはURL)のレスポンスを使い回して配信します。 キーに含まれないヘッダなどでレスポンスが変わると、攻撃者が汚染した1回の応答が保存され、同じキャッシュキーとバリアントを参照し、汚染されたキャッシュノードに到達した利用者へ配られます。 XSSやリダイレクトの混入を広範囲に拡散させる増幅装置として働きます。 一度キャッシュに載ると、攻撃者がアクセスをやめた後も、失効、再検証、退去(eviction)、パージのいずれかが行われるまで汚染が配信される可能性があります。 TTLに達する前に退去やパージが起きる場合もあれば、期限切れのレスポンスを一時的に使う設定によってTTL後も配信される場合もあります。
Webキャッシュポイズニングが起きるパターン
典型的な弱点は次のとおりです。
- キャッシュキーに含まれないヘッダ(
X-Forwarded-Hostなど)の値を、レスポンス内のURL生成に使っている - キーに入らないクエリパラメータやヘッダで、レスポンスの内容が変わる
- エラーページやリダイレクト先に、キーに含まれない入力が反映される
- キャッシュの対象や有効期間の設定が緩く、本来キャッシュすべきでない応答まで保存する
- キー生成に使う要素が、実際にレスポンスを左右する要素と一致していない
共通するのは「キャッシュキーに入らない入力がレスポンスを変え、その結果が使い回される」という点です。
対策法
原則は「レスポンスを左右する入力はすべてキャッシュキーに含め、キーに含まれない入力を応答に混ぜない」ことです。
- レスポンス生成に影響するヘッダやパラメータを、キャッシュキーに正しく反映する。これが本命の対策になる
X-Forwarded-Hostなど署名や検証のないヘッダを、URL生成やレスポンスに使わない- キャッシュする対象と有効期間を明示的に定め、動的な応答を安易にキャッシュしない
- キャッシュキーの正規化を行い、キーに入れる要素とレスポンスを決める要素を一致させる
- 個人向けや認証済みの応答は、キャッシュの対象から明確に外す
キャッシュ層とアプリ層の入力の扱いのずれが原因のため、キー設計の見直しが本命です。 未署名ヘッダの利用箇所を洗い出し、キャッシュキーの正規化とあわせて修正するのが費用対効果の高い進め方になります。
Rails での事例
Rails の request.host は、プロキシ経由の構成では X-Forwarded-Host ヘッダの値を反映します。
この値からページ内の URL を組み立てていると、キャッシュキーに含まれないヘッダがレスポンスの中身を変える、汚染の入り口になります。
<%# 危険:request.host 由来の URL がページに埋まる %>
<%# 攻撃者が X-Forwarded-Host を細工した応答が CDN に保存されると、
同じキャッシュキーと汚染されたキャッシュノードを使う閲覧者が
攻撃者ホストのスクリプトを読み込む %>
<script src="https://<%= request.host %>/assets/app.js"></script>
直し方は二段です。 まずスクリプトや画像の URL は、リクエスト由来ではなく設定値から組み立てます。
# config/environments/production.rb
config.asset_host = "https://assets.example.com"
config.asset_hostはアセットヘルパーが生成するURLに適用される設定であり、request.hostを直接埋め込んだコードは書き換えません。
テンプレート側も、設定を参照するアセットヘルパーへ変更します。
<%= javascript_include_tag "app", defer: true %>
あわせて Host Authorization を本番でも有効にし、想定外のホスト名を持つリクエストをアプリの手前で弾きます。
# config/environments/production.rb
# 本番の config.hosts は既定で空(チェック無効)なので、明示的に設定する
config.hosts = ["www.example.com", "api.example.com"]
config.hosts は開発環境では既定で localhost などに制限されていますが、本番では空のままだと検証が働きません。
リバースプロキシや CDN を挟む構成なら、この設定とキャッシュキーの設計をセットで見直します。
まとめ
Webキャッシュポイズニング対策の要点を整理します。
- 根本原因は「キャッシュキーに含まれない入力が、レスポンスの内容を変えること」であり、汚染された1回の応答が保存されると、同じキャッシュキーとバリアントを参照し、汚染されたキャッシュノードに到達した利用者へ配られる
- 本命の対策はキー設計の見直し。レスポンスを左右する入力はすべてキャッシュキーに含め、キーに入らない入力を応答に混ぜない
X-Forwarded-Hostのように署名も検証もないヘッダを、URL生成やレスポンスに使わない。Railsのrequest.hostはプロキシ構成でこのヘッダを反映する- アセットのURLは
request.hostから組み立てず、config.asset_hostを参照するアセットヘルパーで生成する - Host Authorizationを本番でも有効にする。
config.hostsは開発では既定で制限があるが、本番は空(チェック無効)なので明示的に設定する - 個人向けや認証済みの応答は、キャッシュ対象から明確に外す
- 汚染を確認した場合は、原因を修正したうえでCDNやリバースプロキシの該当キャッシュをパージする
一度キャッシュに載ると、失効、再検証、退去、パージまで汚染が配信される可能性があります。 CDNやリバースプロキシを挟む構成では、キャッシュ層とアプリ層で入力の扱いがずれていないかをキー設計とセットで点検し、事故時に該当キャッシュをパージできる手順も整えてください。
関連して読める記事
キャッシュポイズニングは単体で完結する攻撃ではなく、他の脆弱性を広範囲へ拡散させる増幅装置として働きます。 汚染されたレスポンスの中身は多くの場合XSSであり、リダイレクト先の混入ならオープンリダイレクトの形を取ります。 キャッシュ設定やHost Authorizationの有効化そのものは、設定不備をどう管理するかという運用の問題でもあります。

XSS(クロスサイトスクリプティング)とは?起きるパターンと対策法
XSS(クロスサイトスクリプティング)の仕組みと3つの型、混入しやすいパターン、そして出力時のエスケープを軸にした対策を整理します。React(TypeScript)の dangerouslySetInnerHTML と Rails(ERB)の raw / html_safe を題材に、危険なコードと安全な書き方を具体的なコード例で示します。
2026年7月22日
オープンリダイレクトとは?起きるパターンと対策法
オープンリダイレクトの仕組みと起きるパターン、遷移先を許可リストや内部パスに限定する対策を整理します。Rails 7 の raise_on_open_redirects と、それを allow_other_host: true で無効化してしまう誤った直し方、// で始まるスキーム省略URLの判定、React 側で window.location.href を使う危険例を具体的なコードで示します。
2026年7月25日
セキュリティミスコンフィグとは?起きるパターンと対策法
セキュリティミスコンフィグの仕組みと起きるパターン、安全な既定への統一と環境をまたいだ設定管理を軸にした対策を整理します。Sidekiq の Web UI を認証なしでマウントする危険例と Devise の authenticate で囲む方法、本番で consider_all_requests_local が true になっている場合の露出を具体的なコードで示します。
2026年7月25日