こんにちは!新人のIT担当者や、これからセキュリティの勉強を始めるという開発者の皆さん、日々の開発や運用お疲れ様です。
セキュリティの勉強を始めると、「共通鍵暗号」や「公開鍵暗号」といった小難しい言葉や、なんだか呪文のような英単語がたくさん出てきて、頭がクラクラしてしまいますよね。「一体どこから手を付ければいいんだろう…」と不安になるかもしれませんが、大丈夫です!一歩ずつ、身近な例えから優しく紐解いていきましょう。
今日は、Webサイトを守るための強力な盾である「Content-Security-Policy (CSP)」という仕組みについて、お話しします。
—
1. 家の防犯で考える「CSP(Content-Security-Policy)」ってなに?
いきなりですが、あなたが大切なお宝(顧客データや社内システム)を守る「大きなおうち」に住んでいると想像してみてください。
このおうちには、たくさんの窓やドア(Webページのパーツ)があります。泥棒(攻撃者)の手口で一番怖いのは何だと思いますか? そう、勝手に家に忍び込んで、リビングの真ん中に怪しい置き手紙(悪意のあるスクリプト)を置いたり、勝手にテレビのチャンネルを変えたり(ブラウザを勝手に操作)することです。これが、いわゆる「クロスサイトスクリプティング(XSS)」と呼ばれる攻撃ですね。
従来のセキュリティは、「窓の鍵をしっかり閉めましょう」「怪しい人が来たら追い返しましょう」という対策がメインでした。もちろんそれも大事なのですが、もし泥棒が合い鍵を持っていたり、うっかり窓の鍵を開けっぱなしにしていたら、簡単に侵入されてしまいますよね。
そこで登場するのが、今回テーマにする CSP(Content-Security-Policy) です。
CSPを分かりやすく例えるなら、「我が家に出入りしていい人間は、〇〇さん家の子と、××さん家の子だけ! しかもリビングで勝手に新しい家具(スクリプト)を組み立てちゃダメ!」という、おうちの「厳格なルールブック(契約書)」を玄関のドアにデカデカと貼り出すようなものです。
このルールブックをあらかじめブラウザ(おうちの警備員さん)に渡しておけば、たとえ泥棒が怪しい置き手紙を持ち込もうとしても、警備員さんが「おっと、このルールブックによると、君の書いた手紙は持ち込み禁止だね!」と、パッと没収して守ってくれるんです。
—
2. 攻撃者が狙う「インラインスクリプト」と eval() の恐怖
では、攻撃者は普段どんな手口を使って私たちのWebサイトを狙っているのでしょうか?
よくあるのが、HTMLの中に直接コードを書き込む「インラインスクリプト」や、文字列をプログラムとして無理やり実行してしまう eval() という危険な機能です。
例えば、以下のようなHTMLがあったとします。これは一見なんの変哲もないコードに見えますよね。
<!-- 危険なインラインスクリプトの例 -->
<button onclick="alert('ハックされたよ!');">ここをクリックしてね</button>
この onclick="..." のように、HTMLのなかに直接JavaScriptのコードを埋め込んでしまう書き方を「インラインスクリプト」と呼びます。
攻撃者は、掲示板の書き込み欄やユーザー名入力欄などに、この「怪しいプログラム」をこっそり混ぜ込みます。もしWebサイト側がそれをそのまま画面に表示してしまうと、ブラウザは「おっ、ここにプログラムが書いてあるから実行しちゃおう!」と素直に従ってしまい、攻撃者の仕掛けた罠が発動してしまうのです。
また、eval() という関数も曲者です。これは「渡された文字をなんでもプログラムとして実行しちゃう」という、いわば魔法の箱のようなものですが、攻撃者にこの箱を使われてしまうと、やりたい放題に悪事を働かれてしまいます。
だからこそ、「HTMLの中に直接プログラムを書くのを一切禁止する! プログラムは外部のちゃんとしたファイルとして読み込ませる!」というルールを作る必要があるのです。それがCSPの最大の狙いです。
—
3. CSPの実践:厳格なヘッダーを設定してみよう
前置きが長くなりましたが、実際にWebサーバーからブラウザへ送るCSPの設定(レスポンスヘッダー)を見てみましょう。
今回は、新人の皆さんが実務でそのまま参考にできるように、最も厳格で安全性が高い「モダンなCSP設定」のサンプルをご紹介します。
# HTTPレスポンスヘッダーの例
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self';
なんだか暗号のようで見慣れない文字が並んでいますよね。ひとつずつ、優しく分解して意味を確認していきましょう。
default-src 'self';- 「基本的には、このWebサイト自身(
'self')と同じドメインから読み込まれるファイルしか信用しませんよ」という基本ルールです。 script-src 'self';- 一番重要なディレクティブ(指示)です。JavaScriptなどのスクリプトファイルは、「自分のサイト(
'self')にあるものだけ」実行を許可します。 - これにより、外部の怪しいサーバーからJavaScriptを読み込ませる攻撃や、先ほど説明したインラインスクリプト(HTML内に直接書かれたJS)が一切ブロックされます!
object-src 'none';<object>や<embed>といった古いプラグイン(Flashなど)の読み込みを一切禁止('none')します。もう現代では不要なリスクの温床を完全にシャットアウトします。base-uri 'self';<base>タグを悪用して、相対パスのリンク先を勝手に書き換えられる攻撃を防ぎます。
このように設定しておくだけで、万が一、開発者がうっかり安全ではないコード書いてしまったり、どこからか脆弱性が入り込んだりしても、ブラウザという強力な警備員が不正なスクリプトの実行をガッチリと防いでくれるのです。
—
4. 現場でありがちな失敗と、運用のコツ
「よし、じゃあ今日の夜から早速この一番厳しいCSPを本番環境に適用しちゃおう!」……ちょっと待ってください! ここがインシデントハンドリングや現場の運用で一番やらかしやすいポイントです。
いきなり厳格なCSPを適用すると、長年使ってきた社内ツールや、外部の便利なアクセス解析ツール(Googleアナリティクスなど)、CDNから読み込んでいるデザイン用のCSSやJavaScriptまで「全部まとめてブロック」されてしまい、画面が真っ白になったりボタンが動かなくなったりして、お客様や社内のメンバーから大クレームが来ることになります。
私たちセキュアなエンジニアが現場でどうやっているか、その「泥臭いコツ」をこっそりお教えしますね。
1. まずは「レポート専用モード」から始める
- 最初はブロックするのではなく、「今、このルールに違反する怪しい動きがどれくらいあるかな?」を観測するためのモード(
Content-Security-Policy-Report-Only)を使います。
2. コンソールやログを監視する
- ブラウザの開発者ツールのコンソール画面を見て、意図せずブロックされている正当な外部サービスがないか、じっくり確認します。
3. 必要なドメインだけをホワイトリストに追加する
- 例えば、「Googleアナリティクスは信頼できるから、スクリプトの読み込み先として許可してあげよう」というように、
script-src 'self' https://www.google-analytics.com;のように許可リストを少しずつ調整していきます。
4. 最後に本格適用(Enforce)する
- 誤検知やエラーが出ないことを確認してから、本番の
Content-Security-Policyヘッダーに切り替えます。
—
まとめ
いかがでしたでしょうか?
Content-Security-Policy (CSP) は、一見すると難しそうなルールの羅列に見えますが、本質は「我が家(Webサイト)の安全を守るための、頼もしいルールブック」です。
インラインスクリプトや eval() のようなリスクの高い書き方を排除し、信頼できるソースからのみスクリプトの実行を許可することで、Webアプリケーションのセキュリティレベルは劇的に跳ね上がります。
セキュリティに初めて触れるときは、分からないことばかりで不安になるかもしれませんが、「一つひとつの設定にはちゃんとした理由があるんだな」と分かってくると、パズルを解くようでだんだん楽しくなってきますよ。
ぜひ今日の開発から、少しずつCSPの意識を取り入れてみてくださいね。あなたのWebサイトが、より安全で頑丈なおうちになりますように!
コメント