セッション中のアカウント画面が、本人以外の第三者にそのまま見えてしまう。そんな事態を引き起こすのが「Webキャッシュデセプション(Web Cache Deception)」という攻撃手法です。CDN や リバースプロキシが当たり前になった今、この脆弱性は Webサービスを運営するすべての組織に関係します。
この記事では、Web Cache Deception の仕組み・攻撃者がどのように悪用するか・そして防御のための具体的な手順を、現場で使えるレベルで解説します。「Webキャッシュポイズニングとの違いがよくわからない」「うちのサイトは大丈夫か確認したい」という方にも役立つ内容です。

Webキャッシュデセプションとは?
Webキャッシュデセプション(Web Cache Deception、略称: WCD)は、キャッシュサーバーを騙してユーザーの個人情報や機密データをキャッシュさせ、そのキャッシュを攻撃者が読み取る攻撃です。2017年に Omer Gil 氏が AppSec EU で発表して以降、多くの大手サービスで同様の問題が確認されています。
なぜ今この脆弱性が重要か
現代の Web サービスは CDN(コンテンツデリバリネットワーク)やリバースプロキシを組み合わせてパフォーマンスを高めています。しかし、その「キャッシュ効率を上げる」という仕組みが逆用されるのが WCD です。
・影響範囲: ログイン後のページを持つ Webサービス全般
・攻撃難易度: 技術的な下準備が少なく、URLを細工するだけで試みられる
・被害内容: 個人情報・認証トークン・セッション情報などの漏洩
攻撃の仕組み(敵を知る)
攻撃が成立する条件は、大きく2つです。
条件1: アプリケーション側が不要なパスサフィックスを無視する
たとえば `/account/dashboard/dummy.css` にアクセスしたとき、アプリが `/account/dashboard` と同じレスポンス(マイページの中身)を返す。
条件2: キャッシュサーバーがファイル拡張子で静的コンテンツと判断しキャッシュする
`.css` や `.js`、`.png` といった拡張子が URL に含まれていると、キャッシュサーバーが「これは静的ファイルだ」と判断してレスポンスを保存する。
この2条件が同時に揃ったとき、攻撃は以下の手順で成立します。
# ステップ1: 攻撃者が細工した URL を作成する # 例: https://example.com/account/profile/attacker.css # ステップ2: 被害者をその URL にアクセスさせる(フィッシングメール・罠リンクなど) # ステップ3: アプリが /account/profile の内容(被害者の個人情報)をレスポンスとして返す # ステップ4: CDN・プロキシが .css 拡張子を見て「静的ファイル」と判定し、 # 被害者のレスポンスをキャッシュとして保存する # ステップ5: 攻撃者が同じ URL にアクセスすると、 # キャッシュから被害者の個人情報が返ってくる
Webキャッシュポイズニングとの違い
混同しやすいのが「Webキャッシュポイズニング」です。両者は目的が異なります。
| 攻撃手法 | 何を操作するか | 被害の方向 |
|---|---|---|
| Webキャッシュポイズニング | 攻撃者が悪意あるコンテンツをキャッシュに注入 | キャッシュを閲覧した他のユーザーが被害を受ける |
| Webキャッシュデセプション | 被害者の個人情報をキャッシュに保存させる | 攻撃者がキャッシュを取得して情報を窃取する |
WCD は「被害者を騙してキャッシュさせる → 攻撃者が読む」、ポイズニングは「攻撃者がキャッシュを汚染する → 被害者が読む」というフローの違いがあります。
【想定例】典型的な攻撃シナリオ
以下は架空の想定例です。
あるオンラインサービスでは、`/mypage/settings` にアクセスするとメールアドレスや電話番号が表示されるとします。このサービスが「パス末尾の不明なサフィックスは無視して同じページを返す」という実装だった場合、`/mypage/settings/x.jpg` でも同じマイページが表示されます。CDN の設定で `.jpg` は常にキャッシュ対象とされていれば、フィッシングメールで被害者に `/mypage/settings/x.jpg` を踏ませるだけで、被害者の個人情報がキャッシュに保存されてしまいます。
具体的な防御手順
1. 動的ページに必ず「キャッシュ禁止」ヘッダーを付ける
最も確実な対策です。認証が必要なページ・個人情報を含む全レスポンスに以下のヘッダーを付与します。
# Nginx の場合(ログイン後の location ブロックに追記) location /account { # ユーザー固有のコンテンツはキャッシュさせない add_header Cache-Control "no-store, no-cache, must-revalidate, private"; add_header Pragma "no-cache"; proxy_pass http://backend; } # Apache の場合(.htaccess または VirtualHost 設定)
Header set Cache-Control "no-store, no-cache, must-revalidate, private" Header set Pragma "no-cache"
`no-store` が最重要です。CDN やブラウザが一切キャッシュしないよう指示します。
2. キャッシュサーバーの判定ロジックを拡張子依存から脱却させる
CDN やリバースプロキシのキャッシュ設定を「ファイル拡張子で判断する」のではなく、「`Cache-Control` ヘッダーを信頼する」方式に変更します。
# Varnish の設定例(拡張子ではなくヘッダーで判定する) sub vcl_recv { # Cookie があるリクエストはキャッシュしない if (req.http.Cookie) { return (pass); } } sub vcl_backend_response { # Cache-Control: private / no-store が指定されていればキャッシュしない if (beresp.http.Cache-Control ~ "(private|no-store)") { set beresp.uncacheable = true; } } # Nginx をリバースプロキシとして使う場合 proxy_cache_bypass $http_cookie; proxy_no_cache $http_cookie;
3. アプリケーション側で不明なパスを 404 で返す
「存在しないパスサフィックスを無視して正規ページを返す」という実装が WCD の温床です。ルーティング設定を見直し、定義されていないパスは必ず 404 または 400 を返すようにします。
# Express.js の例(厳密なルートマッチング) # /account/profile 以外のパスは明示的に 404 を返す app.get('/account/profile', authMiddleware, profileHandler); # ワイルドカードで未定義パスを全部 404 にする app.use((req, res) => { res.status(404).json({ error: 'Not Found' }); }); # Django の場合は APPEND_SLASH の動作も確認する # 予期しないリダイレクトが WCD の条件を満たす場合がある
4. Cookie に SameSite 属性を設定する
WCD の攻撃ステップ2では、被害者を細工した URL に誘導する必要があります。`SameSite=Strict` または `SameSite=Lax` を設定することで、外部サイトからの誘導時に Cookie が送信されにくくなり、攻撃の成立条件を一つ崩せます。
Cookie の HttpOnly・Secure・SameSite 属性の詳細な設定方法はこちらの記事で解説しています。
中小企業でも今日からできること
CDN を使っていない中小企業でも、以下の対応は今日からできます。
・サーバーレスポンスヘッダーの確認: ブラウザの開発者ツール → ネットワークタブで、ログイン後のページに `Cache-Control: no-store` が付いているか確認する
・WordPress サイトの場合: `.htaccess` または Nginx 設定でログイン後のパス(`/wp-admin/`、`/my-account/` など)に `Cache-Control: no-store` を追加する
・クラウドサービス(CloudFront・CloudFlare など)の設定確認: キャッシュポリシーが「ファイル拡張子ベース」になっていないか確認し、`Cache-Control` ヘッダーを優先するよう設定を変更する
・外部のWAF・CDNを使っている場合: ベンダーのドキュメントで「origin の Cache-Control ヘッダーを尊重する」設定が有効になっているか確認する
よくある誤解と注意点
【誤解1】HTTPS にすれば大丈夫
HTTPS は通信経路を暗号化しますが、WCD は暗号化されたセッション内の応答をキャッシュさせる問題です。HTTPS の有無に関わらず発生します。
【誤解2】静的ファイル(CSS・画像など)を CDN でキャッシュしているだけだから安全
問題は「動的ページが静的ファイルに見えるよう細工した URL を踏まされる」点にあります。CDN が静的ファイルしかキャッシュしていなくても、アプリが不正な URL サフィックスを無視して動的コンテンツを返すなら、それがキャッシュされてしまいます。
【注意】一般的な誤検知パターン
WCD の脆弱性スキャンでは「存在しないパスにアクセスして 200 が返る」ことを確認しますが、ページが 200 を返しても `Cache-Control: no-store` が適切に付いていれば実害には至りません。発見した際は「ヘッダーが適切か」まで合わせて確認するのが正確な評価です。

本記事のまとめ
| 観点 | ポイント |
|---|---|
| 攻撃の本質 | CDN などのキャッシュを騙してユーザー固有のレスポンスを保存させ、攻撃者が読み取る |
| 成立条件 | ①アプリが不正パスサフィックスを無視して正規ページを返す + ②キャッシュが拡張子でキャッシュ判定する |
| 最優先の対策 | 動的ページ全体に Cache-Control: no-store, private を付与する |
| キャッシュ設定 | 拡張子ではなく Cache-Control ヘッダーを信頼するよう CDN・プロキシを設定する |
| アプリ側 | 未定義パスは 404 を返し、無視しない実装にする |
WCD は知らなければ気づきにくい脆弱性ですが、対策の本質は「動的ページをキャッシュさせない」というシンプルな原則です。CDN の導入・リバースプロキシの設定変更のタイミングで、必ず Cache-Control ヘッダーの設定を確認する習慣をつけておきましょう。
PR
体系的に学ぶ 安全なWebアプリケーションの作り方 第2版(徳丸浩/SBクリエイティブ)
SQLインジェクションや XSS といった定番の脆弱性から、キャッシュの誤用まで Webアプリのセキュリティを体系的に解説した定番書。開発者・情シスどちらにも役立ちます。
