セキュリティ
Content Security Policyについて語る
Yuya Kanesawa
はじめに
Content Security Policyという言葉を聞いたことがあるでしょうか?
昨今Webサービスの増加が著しいですが,それと共にWebのセキュリティ対策について懸念されています.XSS(クロスサイトスクリプティング)やSQLインジェクション,CSRF(クロスサイトリクエストフォージェリ)など,Webサービスを作るにあたって様々な脆弱性に気をつける必要がありますが,きちんとセキュリティ対策ができていると断言できる企業の数はどのくらいあるのでしょうか?
世の中で一般的に使われている製品やライブラリにも脆弱性が見つかるこの時代,自社のWebアプリケーションには脆弱性など存在しないと思っていても,一抹の不安を残す企業は少なくないはずです.
CSPとは
コンテンツセキュリティポリシー (CSP) は、クロスサイトスクリプティング (XSS) やデータインジェクション攻撃などのような、特定の種類の攻撃を検知し、影響を軽減するために追加できるセキュリティレイヤーです。これらの攻撃はデータの窃取からサイトの改ざん、マルウェアの拡散に至るまで、様々な目的に用いられます。
引用:https://developer.mozilla.org/ja/docs/Web/HTTP/CSP
MDNではこのように述べられています.XSSという脆弱性があると,悪意のあるユーザーによって仕込まれた任意のJavaScriptコードが実行されてしまい,上記に書いてあるような危険があります.XSSはエスケープするべき特殊文字をエスケープし忘れることに起因する脆弱性ですが,開発者が作りやすい脆弱性でもあるため,難しい問題です.
CSPはこのような状況を打破するために考案されたものです.CSPが導入されている場合,ブラウザが悪意のあるJavaScriptコードの実行を阻止することで,Webサイトに脆弱性があったとしてもユーザーが危険な目に合う可能性が大幅に減少します.
CSPを導入するメリット・デメリット
メリットは?
一番のメリットはやはり保険的対策になるということではないでしょうか.個人的には,XSSについて開発者が気にしなくてよくなるというのはメリットではないと思っています.なぜなら,脆弱性に対して気を配りながらコーディングするのが普通だと思うからです.
例えばCSPが導入されているからといって,開発者がセキュアコーディングを意識しなくなったらどうなるでしょうか?CSPが導入されていない環境でコーディングするときにも脆弱性に対して気を配れなくなってしまわないでしょうか?
私はそのような状況を危惧しているため,XSSについて開発者が気にしなくてよくなるというのはメリットではないと考えます.”セキュアコーディングを心掛けていたけど意図せず作り込んでしまった脆弱性”に対して有効だというのが正しいと思います.
脆弱性が見つかってから修正し,次から脆弱性を作りこまないためにどうすればいいかを考えるような受動的な対応ではなく,より能動的にセキュリティ対策を行えることもメリットの一つです.
デメリットは?
導入に時間がかかることや開発者の理解不足などが挙げられます.
一口に「CSPを導入しよう」と言っても,作業量は導入対象のWebアプリケーションの規模に比例します.例えばそれが大規模なWebアプリケーションであれば,導入は容易ではなく,ソースコードの大きな変更が要求されるでしょう.
セキュリティ担当のものが開発者に対して「CSPを導入しよう」と頼んでも,そのような大きな変更は開発者自身が嫌うこともあり,理解が得られないと難しいです.
開発者に対して,脆弱性が存在した場合の危険性や,CSPを導入することでどのようなメリットがあるかをきちんと説明して理解を得ることが大切だと思います.
また,デメリットではないですが,CSPを導入することでXSS対策が完璧になるとは言えません.設定ミスが原因でバイパスされてしまったり,そもそもCSPの仕組みをバイパスするハッカーが現れるかもしれません.完璧な対策は存在しないので,多層防御という考え方に沿ってより多くの対策を講じることが重要です.
CSPを導入してみる
ここまで,CSPの導入についてあまり具体的な話はしてこなかったので,簡単なWebアプリケーションを作ってみてCSPを導入するまでの過程をご紹介します.
導入する対象はRuby on Rails製の簡易アプリケーションとします.と言っても,scaffoldで秒で作ったToDoアプリです.
ToDoリストを管理できるようになっていて,New To Doから新しいToDoを追加することができます.Checkは完了したかどうかの判定に付けてみました.
Contentの部分に内容を記入して,Create To doで作成できます.試しにここに悪意のあるコードを入れてみます.
アラートが実行されました.Contentに入力した文字列は
<script>alert(1)</script>
という風にscriptタグで囲ったものです.scriptタグに囲まれたalert(1)が実行されているということは,任意のJavaScriptコードが実行可能な状態であり,新規作成したToDoがデータベースに格納されるこのXSSはStored XSSというタイプに該当します.
なぜこのようなことが起こるのでしょうか?上でも述べましたが,基本的にエスケープ漏れが原因であり,この場合はContentの内容をレンダリングしている箇所でエスケープせずにrawメソッドを用いてそのまま出力してしまっていることが原因です.
<p id="notice"><%= notice %></p>
<p>
<strong>Content:</strong>
<%= raw(@to_do.content) %>
</p>
<p>
<strong>Check:</strong>
<%= @to_do.check %>
</p>
昨今のフレームワークでは既に対策がされていて,あまりセキュリティを意識せずに作ったとしてもフレームワークがよしなにやってくれるため,基本的にこのようなことが起こることは稀です.しかし,どうしても複雑なことをやろうとしたり,フレームワークに頼らずに自前で実装している場合などに脆弱性を作り込む危険性が高まります.
さて,このWebアプリケーションにCSPを導入してみます.今回はRails製のアプリケーションなので,いい感じのGemがないか探してみたところ,secureheadersというGem( https://morizyun.github.io/ruby/library-secureheaders.html )を見つけました.今回はこれを使用してみます.
また,Rails5.2からはGemを使わずにデフォルトの設定から有効にできるようになっているらしいです.( https://qiita.com/ryohashimoto/items/75f48fa5a3846c58f735 )
Gemfileに
gem 'secure_headers'
を追加し,
bundle install
します.
その後,
config/initializers/secure_headers.rb
を以下の内容で作成します.
SecureHeaders::Configuration.default do |config|
config.csp = {
default_src: %w('self'),
script_src: %w('self'),
}
end
ここでは defualt-src
ディレクティブと script-src
ディレクティブを指定しています. default-src
は他のディレクティブが存在しなかったときに使うもので, script-src
はscript関連の権限を制御するディレクティブです.どちらもselfを指定していますが,これはそのWebページのオリジンからしか読み込まないという意味であり,インラインスクリプトの実行も禁止します.
この状態でもう1度,悪意のあるJavaScriptコードを挿入してみます.
アラートは実行されませんでした.Consoleタブに何か出ていることがおわかりいただけるでしょうか?
script-src
のselfに違反するためにインラインスクリプトの実行が拒否されていることがわかります.このように,CSPを導入することでXSSを防ぐことができました.
現在のCSPで推奨されていること
CSPは現在Level3が最新ですが,Level3で推奨されていることは nonce + strict-dynamic というものです.Level2までで推奨されてきたのはドメインのホワイトリスト方式でした.つまり,ここのURLからしかスクリプトを読み込まないという風にURLを列挙していくということです.
ですが,この方法はバイパスすることが可能だということがわかっていて,現在ではnonceの使用が推奨されています.
nonceとはなにかというと,それぞれのscriptタグにnonceを付与していき,その値がHTTPレスポンスヘッダに存在するnonceと同じものだけ実行を許可するというものです.
<scirpt nonce="m7QkHEGc...." src="..." > </script>
<script src="..." ></script>
<script>alert(1)</script>
この場合,上のscriptタグは実行され,下の2つのscriptタグは実行されません.
また,strict-dynamicを使うとnonceが付与されたscriptタグ内で動的に生成されたscriptも正常に動作するので,Webアプリケーション本来の動作に影響を及ぼさずにCSPを導入することが可能になります.
ここら辺の話は以下の記事がとてもわかりやすいです.
導入するにあたって気をつけること
Webアプリケーション本来の動作に影響を与えないようにすることです.例えばGoogleタグマネージャーなど,外部から読み込んでいるスクリプトはCSPの影響を受けて動作しなくなることもあります.
また,ブラウザごとに出し分けを行うことも挙げられます.上記のstrict-dynamicはサポートしていないブラウザがあったりするので,ユーザーエージェントを見ながら出し分けを行う必要がありそうです.
具体的な導入手順としては,推奨されている3つのディレクティブ(script-src, object-src, base-uri)を設定してから,CSPをReport Onlyモードで動かします.Report Onlyモードは違反があったとしてもそれをブロックせず,設定したreport-uriにログを残すだけなので,Webアプリケーションの動作に影響を与えないため,まずはこちらのReport Onlyモードで試してみるというのが得策です.
その後,挙がってくるCSP違反レポートを見ながら必要なディレクティブを適宜追加していき,最終的にReport Onlyを外すというのが妥当だと思います.
まとめ
ここまで,CSPの基本的な機能から導入手順,導入時に気を付けることなどを紹介しました.
CSPを導入することでXSSなどの攻撃を防ぐことができ,より能動的なセキュリティ対策が可能になります.
気になった方は是非一度,導入を検討してみてはいかがでしょうか?
以上,CSPの紹介でした.