CSP (Content Security Policy) nonce-sourceについて調べてみた

CSP nonce-sourceとは?

Content Security Policy (CSP) は、クロスサイトスクリプティング (XSS) やデータインジェクション攻撃を含む、よく知られた種類の攻撃を検出して軽減する、セキュリティの追加レイヤーです。これらの攻撃手法は、データ窃盗からサイト改変、マルウェア感染まで、すべてに使用されます。 https://developer.mozilla.org/ja/docs/Security/CSP より。

XSSに関して簡単に言うと、実行できるJavaScriptを制限して、外部からスクリプトが注入されてもブラウザが実行しないことでXSSを防御する機能ということになります。

例えば、自分のサーバ上のjsファイルからのみのscriptを実行するように指定すれば、外部からスクリプトを挿入されても実行はされず安全になります。

しかし、インラインのJavaScriptを一切禁止してしまうと色々と面倒です。

そこで、実行してよいscriptタグにはnonce属性でリクエストごとにワンタイムな「ランダムな文字列」を指定し、その値をCSPヘッダに指定するというのが、nonce-sourceです。CSP 1.1で導入されました。

例えば、HTTPレスポンスヘッダ(CSPヘッダ)で

Content-Security-Policy: script-src 'nonce-sXXD/nluT6AqhuVwL0IJqA=='

と指定します。sXXD/nluT6AqhuVwL0IJqA==がnonceの値でbase64値です。この場合、HTMLの中に以下の2つのscriptタグがあると、

<script type="text/javascript" nonce="sXXD/nluT6AqhuVwL0IJqA==">
    // ここはnonceが正しいので実行される
    alert('This works!');
</script>

<script type="text/javascript">
    // ここはnonceがない(nonceが不正)ので実行されない
    alert('This does not work!');
</script>

ということになります。

もともとHTMLソースにある正当なscriptタグはnonceを指定することで実行されますが、外部から不正に注入されたscriptには正当なnonceがないので実行されない、というわけです。

なかなかよさそうですが、実際に導入するにはブラウザの対応状況などを確認する必要があります。ということで調べてみました。

ブラウザの対応状況の検証結果

script-src nonce

CSPのscript-srcにnonceのみを指定した場合です。

Content-Security-Policy: script-src 'nonce-$RANDOM'
OS ブラウザ バージョン nonce付きscriptタグ nonceなしscriptタグ 判定
Windows Firefox 33.0.1 実行 実行されず
Linux Firefox 33.0 実行 実行されず
Mac Firefox 33.0 実行 実行されず
Windows Firefox 23.0.1 実行 実行 ×
Windows Firefox 22.0 実行 実行 ×
Linux Chrome 38.0.2125.104 実行 実行されず
Mac Chrome 38.0.2125.104 実行 実行されず
Linux Chromium 37.0.2062.120 実行 実行されず
Linux Opera 12.16 実行 実行 ×
Mac Safari 8.0 (10600.1.25) 実行されず 実行されず ×
Windows IE 11.0 実行 実行 ×

Firefox 33、Chrome(Chromium)37/38は正常にnonce-sourceをサポートしています。

Opera、IEはnonce-sourceをサポートしておらず、nonceがないscriptも実行されます。

Safariはnonce-sourceをサポートしてないだけでなく、scriptが全く実行されなくなってしまっています。SafariはCSP 1.0には対応しているため、このような結果になるようです。

このCSPヘッダでは、CSP 1.0のみ対応のブラウザでscriptが全く実行されなくなる不具合が生じてしまうようです。

script-src unsafe-inline and nonce

フォールバックのために、CSPのscript-srcにunsafe-inlineとnonceの両方を指定した場合です。

Content-Security-Policy: script-src 'unsafe-inline' 'nonce-$RANDOM'
OS ブラウザ バージョン nonce付きscriptタグ nonceなしscriptタグ 判定
Windows Firefox 33.0.1 実行 実行 ×
Linux Firefox 33.0 実行 実行 ×
Mac Firefox 33.0 実行 実行 ×
Windows Firefox 23.0.1 実行 実行 ×
Windows Firefox 22.0 実行 実行 ×
Linux Chrome 38.0.2125.104 実行 実行されず
Mac Chrome 38.0.2125.104 実行 実行されず
Linux Chromium 37.0.2062.120 実行 実行されず
Linux Opera 12.16 実行 実行 ×
Mac Safari 8.0 (10600.1.25) 実行 実行 ×
Windows IE 11.0 実行 実行 ×

Chrome(Chromium)はnonceのみの場合と同じですが、Firefox 33ではnonceがないscriptも実行されてしまっています。これはFirefoxのバグでしょうか?

Firefox 22、Opera、Safari、IEはnonce-sourceをサポートしていないため、すべてのscriptが実行されています。

このCSPヘッダでは、CSP 1.0対応のSafariでもscriptが実行されなくなってしまう不具合はなくなりましたが、nounce-sourceに対応しているはずのFirefoxで保護が無効になってしまっています。

調査結果のまとめ

nonce-sourceに対応しているブラウザでは保護が働き、未対応のブラウザでは今までどおりscriptがすべて実行されればよいのですが、

  • Content-Security-Policy: script-src 'nonce-$RANDOM'のみだと、CSP 1.0対応のSafariでscriptが全く実行されなくなる不具合が生じる
  • Content-Security-Policy: script-src 'unsafe-inline' 'nonce-$RANDOM'とすると対応しているはずのFirefoxで保護が無効になる

という状況です。なかなかうまくいきませんね。

といくことで、ChromeとFirefoxの対応バージョンにのみ、CSPヘッダを出力するのが現実的かなと思います。

nonce-sourceに対応したChromeのバージョンっていくつなんでしょう?わかる方いましたら、お教え願えるとありがたいです。

Firefoxは31のようなのですが。

ブラウザの対応状況の検証方法

検証と実装のためのリポジトリをGitHubに用意しました。

ソースコードをインストールします。

$ git clone https://github.com/kenjis/php-csp-nonce-source.git
$ cd php-csp-nonce-source
$ composer install

PHPのビルトインWebサーバを起動します。

$ php -S localhost:8000

http://localhost:8000/check/ にアクセスし、順にリンクをクリックします。

nonce付きのscriptタグが実行されると「This works!」とアラートが出ます。このアラートが出るのは正常です。

nonceなしのscriptタグが実行されると「This does not work!」とアラートが出ます。このアラートが出るということは、ブラウザがnonce-sourceに対応していないということになります。

参考

Date: 2014/10/28

Tags: php, csp, security, xss