穴の開いたバケツで水を汲むな:CSPレポートエンドポイントを「攻撃者の踏み台」にしないための鉄則
現場でインシデント対応をしていると、セキュリティ設定を「とりあえず有効化した」だけの状態で放置しているシステムに頻繁に遭遇する。特にCSP(Content Security Policy)の導入は、XSS対策の切り札として推奨されるが、「違反レポートを受け取るためのエンドポイント」自体を脆弱なまま放置しているケースが驚くほど多い。
君たちが「攻撃の予兆を検知するため」に作ったはずのレポート受信サーバーが、実は攻撃者に悪用される「攻撃用の踏み台」や「負荷分散攻撃の標的」になっているとしたら、笑い話にもならないだろう。今日は、CSPレポートエンドポイントを堅牢に構築し、真の意味で「戦える監視基盤」にするための技術を叩き込む。
—
なぜCSPレポートエンドポイントが狙われるのか?
CSPレポート(report-uri / report-to)は、ブラウザが違反を検知した際に、JSON形式のデータを指定されたURLにPOSTする仕組みだ。攻撃者はここを次のように悪用する。
1. POSTによるDoS攻撃: エンドポイントに大量の不正なJSONを投げつけ、バックエンドのデータベースやログ収集基盤をパンクさせる。
2. インジェクションの踏み台: レポート内容を偽装し、ログ収集用のDBに対してSQLインジェクションやOSコマンドインジェクションを仕掛ける(ログ解析ツールが脆弱な場合、そこが突破口になる)。
3. 情報漏洩: 意図的に違反を発生させ、レポート内容に含まれる機密性の高いURLパラメータや認証トークンを収集する。
つまり、「外部からPOSTを受け取る」という行為自体が、すでにハイリスクな操作であることを肝に銘じてほしい。
—
堅牢な実装サンプル:Goによる超軽量・高耐久レシーバー
PHPやPythonでも実装は可能だが、大量のトラフィックをさばきつつ、メモリ消費を抑え、型安全性を確保するという観点から、今回はGoでの実装例を示す。このコードは、「入力を徹底的にバリデートし、即座に安全なログへ落とし込む」ことに特化している。
実装コード(main.go)
package main
import (
“encoding/json”
“log”
“net/http”
)
// 受信するCSPレポートの構造体を厳密に定義する
type CSPReport struct {
Report struct {
DocumentURI string json:"document-uri"
BlockedURI string json:"blocked-uri"
ViolatedDir string json:"violated-directive"
} json:"csp-report"
}
func reportHandler(w http.ResponseWriter, r http.Request) {
if r.Method != http.MethodPost {
http.Error(w, “Method Not Allowed”, http.StatusMethodNotAllowed)
return
}
// 1. レスポンスボディのサイズを制限(DoS対策)
r.Body = http.MaxBytesReader(w, r.Body, 102410)
var report CSPReport
if err := json.NewDecoder(r.Body).Decode(&report); err != nil {
http.Error(w, “Bad Request”, http.StatusBadRequest)
return
}
// 2. ログには構造化データとして出力し、インジェクションを防止する
// 直接DBに書き込まず、必ず標準出力経由でfluentd/logstash等へ渡す設計にする
log.Printf(“CSP_VIOLATION: doc=%s, blocked=%s, dir=%s”,
report.Report.DocumentURI, report.Report.BlockedURI, report.Report.ViolatedDir)
w.WriteHeader(http.StatusNoContent)
}
func main() {
http.HandleFunc(“/csp-report”, reportHandler)
log.Fatal(http.ListenAndServe(“:8080”, nil))
}
—
実務で不可欠な3つの防御レイヤー
コードを書くだけでは不十分だ。インフラレイヤーで以下の3点を必ず設定せよ。
1. WAF(Web Application Firewall)による保護
AWS WAFやCloudflareを使用している場合、レポートエンドポイントに対しては以下の設定を適用する。
- リクエストレート制限: 特定のIPからのPOSTリクエストを1分間に数回程度に絞る。
- シグネチャフィルタ:
eval()やjavascript:などの文字列が含まれる場合にブロックするルールを適用する。
2. Nginxでのリクエスト制限(Rate Limiting)
アプリケーションの手前でNginxをプロキシとして挟んでいるなら、以下の設定は必須だ。
limit_req_zone $binary_remote_addr zone=csp_limit:10m rate=5r/s;
server {
location /csp-report {
limit_req zone=csp_limit burst=10 nodelay;
proxy_pass http://localhost:8080;
}
}
3. セキュリティヘッダーの付与
レポートエンドポイント自体も、攻撃の踏み台にされないよう以下のヘッダーを付与する。
X-Content-Type-Options: nosniffContent-Type: application/json(これ以外を受け付けない)
—
結論:監視は「受動的」であるべきではない
CSPレポートエンドポイントは、君たちの城を守るための「防犯カメラ」だ。しかし、カメラそのものが破壊されれば、侵入者はやりたい放題になる。
「外部からの入力を信用しない」というセキュリティの鉄則は、どんなに地味な監視用エンドポイントであっても例外ではない。今回紹介したバリデーションとレート制限を組み込み、ログ基盤自体を「セキュアなパイプライン」として設計すること。
もし、今動いている監視システムが「とりあえずログをDBに溜め込んでいるだけ」なら、今すぐ修正に着手してくれ。セキュリティは、コードを書き終えた後に始まるのではなく、アーキテクチャを決めた瞬間に勝負が決まっているのだから。
コメント