脆弱性の「穴」を塞ぐ最後の砦:CSPの『script-src』で防ぐインジェクションの終わり
現場でインシデント対応をしていると、SQLインジェクションやXSS(クロスサイトスクリプティング)を「古い時代の遺物」だと高を括っているエンジニアによく出会う。だが、現実はどうか。モダンなフレームワークを使っていても、ちょっとした実装の隙間や、外部ライブラリの汚染(サプライチェーン攻撃)で、アプリケーションは平気で任意のコードを実行させられてしまう。
特に厄介なのが、攻撃者が仕込んだインラインスクリプトや、悪意あるドメインからのスクリプト読み込みだ。これを防ぐための最後の防波堤が、今回解説する Content-Security-Policy (CSP) の script-src ディレクティブだ。教科書的な説明は省く。現場で「なぜこれを設定しないと眠れないのか」、その真髄を話そう。
—
1. なぜ『script-src』が攻撃者の心臓を止めるのか
攻撃者がXSSを狙う際、最もやりたいのは「ブラウザに自分の書いたコードを勝手に実行させること」だ。
- インラインスクリプト:
のような直接記述。 - eval(): 実行時に動的に文字列をコードとして解釈させる手法。
CSPの script-src は、これらを「物理的に不可能」にする。ブラウザに対して「このサイトで実行を許可するのは、俺たちが認めたスクリプトだけだ」と強制的にルールを叩き込む仕組みだ。これを導入するだけで、万が一アプリケーション側にXSSの脆弱性が残っていても、攻撃者のペイロードはブラウザによって「無視」される。
—
2. 実践:最強の『script-src』設定例
まずは、Nginxの設定ファイル(nginx.conf やサイト設定)に記述する、モダンでセキュアなCSPヘッダーの例を見てほしい。
NginxでCSPヘッダーを付与する設定
‘self’ は自ドメインのみを許可
‘strict-dynamic’ を使うことで、信頼できるスクリプトが読み込んだ子スクリプトも許可できる
nonceは後述するが、動的なインラインスクリプトを安全に許可する鍵となる
add_header Content-Security-Policy “default-src ‘self’; script-src ‘self’ ‘nonce-random123’ ‘strict-dynamic’; object-src ‘none’; base-uri ‘self’;” always;
なぜこの設定なのか?
'self': 自分のサーバー内にあるスクリプトしか読み込ませない。これだけで外部からの怪しいJS読み込みは即死する。'nonce-random123': サーバーサイドで生成したランダムな文字列(nonce)を持つスクリプトタグだけを許可する。これがないと、攻撃者がどんなに巧妙なタグを挿入してもブラウザは実行を拒否する。'strict-dynamic': これが今のトレンドだ。信頼できるソースから読み込まれたスクリプトが、さらに別のスクリプトを読み込むことを許可する。レガシーな設定で苦労する「複雑な依存関係」を解決してくれる。
—
3. 実装のキモ:サーバーサイドでのNonce生成(PHPの例)
CSPを導入する際、最も躓くのが「どうやって一意のNonceを生成して、テンプレートに埋め込むか」だ。PHPを例に、セキュアな実装パターンを示す。
この実装の肝は、Nonceがリクエストごとに毎回変わることだ。攻撃者が「さっきのNonce」を盗んだとしても、次のリクエストでは無効化されているため、継続的な攻撃は不可能だ。
—
4. 現場の教訓:導入時の「破壊」を恐れるな
「CSPを入れると既存の画面が動かなくなる」という恐怖があるだろう。その通りだ。導入初期は、ブラウザのコンソールにエラーが溢れかえる。
そこで活用すべきなのが Content-Security-Policy-Report-Only ヘッダーだ。これを使うと、「もしこのポリシーを適用したら、どこでブロックが発生するか」をレポートとしてサーバーに送信できる。
開発・テスト時はこれで運用し、エラーログを収集する
add_header Content-Security-Policy-Report-Only “default-src ‘self’; script-src ‘self’; report-uri /csp-violation-report-endpoint”;
これを数日間運用し、ブロックされている正当なライブラリやスクリプトを特定してから、本番適用(Content-Security-Policy)に切り替える。これが最も泥臭く、かつ最も安全な移行手順だ。
—
最後に:防御は「多層」で考える
CSPは万能ではない。しかし、インジェクション攻撃において、攻撃者の「最後の出口」を塞ぐ非常に強力なツールだ。
コードを書き終えて満足するな。「もし自分の書いたコードに脆弱性があったら?」という最悪のケースを常に想定し、ブラウザというクライアント環境に制御権を委ねるのが、プロのエンジニアの流儀だ。今日から、君たちのWebサイトのヘッダーを確認してほしい。CSPが入っていないなら、それはまだ「鍵の開いた家」に住んでいるのと同じことだ。
コメント