tokuhirom's Blog

Content-Security-Policy と nonce の話

Content-Security-Policy の nonce を利用すると、XSS の脅威をかなり軽減できます。

そこで、Web Application Framework ではデフォルトで対応したほうがよいのではないか、という旨を @hasegawayosuke さんから教えて頂いたので、実装について考えてみました。

とりあえず CSP の nonce はどういうものなのかを考慮するために、コード例を探していたのですが、実際に動くサンプルというものが nonce 関連のもので見当たりませんでした。

そこで、実際に動くサンプルを用意しました。

https://github.com/tokuhirom/csp-nonce-sample

以下は Sinatra で書かれたサンプルコードです。

require 'sinatra'
require 'securerandom'

get '/' do
    @nonce = SecureRandom.base64()
    headers 'Content-Security-Policy' => "script-src 'nonce-#{@nonce}' 'unsafe-inline'"
    erb :index
end

テンプレートファイルは以下のようになります。

<!doctype html>
<html>
    <body>
        <h1>CSP の nonce のサンプルです。</h1>

        <script type="text/javascript" nonce="<%= @nonce %>">
            alert("これは信用されてる");
        </script>
        <script type="text/javascript">
            alert("こんにちは!こんにちは!");
        </script>
        ↑これは、攻撃者が送り込んだメッセージだと思ってください。これは実際にはサポートされているブラウザでは実行されません。
    </body>
</html>

"Content-Security-Policy: unsafe-inline; script-src 'nonce-${nonce}'" 形式で書いたら、${nonce} の部分を script タグの属性にいれないと実行されなくなります。

nonce の値は、リクエストごとにランダムな文字列が生成されるので、クライアント側からは想像もつかず、攻撃することが困難になります。

これはもう、どんどん nonce 使って行った方がいいのでしょうかね!

Web application framework でのサポートについて

Web Application Framework でこの機能をがっつりサポートするのはどうなのでしょうか?

現実的には HTML をパースして nonce を埋め込むアプローチも考えられますが、それでは攻撃者が送ってきた script タグに nonce をつけてしまう可能性もあって効果が薄れます。

理想的には、やはり手作業で一つ一つ nonce 値を埋めていって上げるのがよいでしょう。

Web Application Framework では csp header の送出とテンプレートエンジンへの nonce 値の受け渡しだけを担当し、HTML の書き換えはユーザーに任せたほうがよいように思います。

(追記) unsafe-inline をつけよう

ということなので、unsafe-inline もつけたほうがよいようです。

(追記) nonce つけても XSS される場合。

<script nonce="...">document.write(location.hashref)</script>

のような場合、ダメとのこと。