画像変換 CDN で CSP が外れる:Cache-Control の引き継ぎと取得元リダイレクトを塞ぐ
Cloudflare の /cdn-cgi/image/ で、SVG サニタイザーの不備、Chromium のキャッシュ処理、取得元制限のリダイレクト回避をつないだ XSS が実証された。画像変換・画像 CDN を使うなら、取得元を狭く、オープンリダイレクトを持たず、元画像の Cache-Control や Content-Type を上書き・転送させない構成にする。
3 つの要素(Searchlight Cyber の報告)
- SVG サニタイザーの不備: Cloudflare の
svg-hushが使う XML ライブラリーxmlで、名前空間宣言の値がエスケープされずに書き戻され、検査後にスクリプトが混入した。xml1.4 以降で修正済みで、現行の main では再現しないと Cloudflare は説明。 - Chromium のキャッシュで CSP が消える: 画像変換エンドポイントは元画像の
Cache-Controlを引き継ぐ。no-cache="Content-Security-Policy"のようにヘッダー名を指定すると、Chromium はそのヘッダーを除いてディスクキャッシュに保存する。初回表示では CSP が効くが、「戻る」でキャッシュから読ませるとスクリプトが動く。Firefox と Safari にはない Chromium 固有の挙動。 - 取得元制限の回避: 既定の「This zone only」でも、取得処理がリダイレクト先を再検査しないため、許可された取得元にオープンリダイレクトがあれば外部の画像を読める。ゾーン内のどこかで Cloudflare Access を有効にしていると、そのログアウト用エンドポイントから
cloudflare.com配下のオープンリダイレクトへつなぐ経路で成立し、www.cloudflare.com上の XSS まで実証された。
同じ考え方が通る他サービス
- Netlify Image CDN: 取得元の許可が広すぎると、同じ
Cache-Control指定で CSP とContent-Typeをキャッシュから外し、HTML として解釈させられる。 - S3 の署名付き URL: 公開オブジェクトに第三者が自分の認証情報で署名した URL を作ると、
Content-Typeの上書きに加えてCache-Controlの上書きで CSP を外せる。クエリー文字列をそのまま渡す独自ドメインやリバースプロキシー経由の配信が対象。
対策
- 画像変換の取得元は自ゾーン内でも必要なパスに絞る。許可した取得元にオープンリダイレクト(ログイン後・ログアウト後の
redirect=など)がないか点検する。 - 画像配信用のホスト名を、アプリ本体と Cookie を共有しない別オリジンに分ける。XSS が成立しても本体のセッションに届かないようにする。
- 元画像や上流の応答の
Cache-Control・Content-Type・Content-Dispositionを、そのまま利用者に返さない。配信側で固定値に上書きする。 - S3 などをリバースプロキシーや独自ドメインで出すときは、
response-cache-controlやresponse-content-typeなどの上書き用クエリーを通さない(署名付き URL をクエリーごと転送しない)。 - SVG はできればラスター化して返すか、
Content-Type: image/svg+xmlでもContent-Disposition: attachmentとX-Content-Type-Options: nosniffを付けて直接表示させない。 - CSP はヘッダーだけでなく、キャッシュ経由で外れる可能性がある前提で、同じオリジンに利用者のアップロード物を置かない設計を優先する。