ホームページで使うJavascriptをセキュアに使うnonce属性についての話

2021年6月20日

Javascript テクノロジー

eyecatch 他人のサイトを見て、javascriptを勉強している、弓削田です。 Googleサイトを見ていると、scriptタグに、nonceという属性がついているのがあったので、調べてみたら、 ワンタイムコードを発行してそれをscriptタグと連携することで、そのワンタイムコードが書かれていないscriptタグは実行ができないというモノでした。 このnonce属性は、セキュアなメリットもある一方で、デメリットもあるので、その注意点と、簡単なソースコード、JSaas(JavascriptのSaas)というwebサービスを数多く作ってきた経験から判断してみたいと思います。

nonce属性の搭載サンプル

scriptタグのnonceセットは、 サーバー側のapacheやnginxなどで出力するレスポンスヘッダに書き込むパターンと、 phpでhtml出力をする時のheaderに書き込むパターンと、 htmlのmetaタグに書き込むパターン この3種類ありますが、今回は、最も導入がしやすそうなmetaタグに書き込んで試してみます。 <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> <meta charset="utf-8"> <title>nonce</title> <style> </style> </head> <body> <div id="a"></div> <script>document.getElementById("a").innerHTML = "test-a";</script> <div id="b"></div> <script nonce="{{nonce}}">document.getElementById("b").innerHTML = "test-b";</script> </body> </html> <?php $key = random_bytes(20); $nonce_code = base64_encode($key); $source = file_get_contents("./index.html"); $source = str_replace("{{key}}" , $key , $source); $source = str_replace("{{nonce}}" , $nonce_code , $source); echo $source; 上記2つのファイルをサーバー(または、ローカルPHP環境)に配置して、index.phpを表示すると、 "test-b" とだけ表示されているのがわかります。 本来"test-a"というスクリプトも実行されていますが、こちらはnonce属性が無いため、実行されていないのがわかります。 ブラウザコンソールのログをみてみると、
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'nonce-SDNX3jv+evDjcYEp1nX3GX08c8k='". Either the 'unsafe-inline' keyword, a hash ('sha256-AsBDePwR00/5X4ajQrs/lRISK6Bbtqgjxu9O0OJxGJQ='), or a nonce ('nonce-...') is required to enable inline execution.
「nonceポリシーがあるので、scriptタグが実行できんかったよ!」と言っているようです。(テキトー訳)

nonce属性の設置ポイント

このポリシーを定義してしまうと、nonce属性で正常にサーバーから吐き出されるワンタイムコードが表記されなければ、実行ができないので、上記のサンプルのように、簡易に実行したいコードなども実行されなくなってしまいます。 かなりクローズドな構成のwebサイトでは有効ですが、nonce属性が付けられないscriptタグがどうしても必要な場合は、ポリシーを設定しないほうがいいでしょう。 ちなみに、Saas製品などのscriptタグを貼りたい時は、各種のタグそれぞれに、nonce属性を付けてあげると、そのscriptタグは実行できるようになりますが、そのサービスがscriptライブラリなどを読み込む処理の場合に、それらにnonce属性がついていない場合は、実行がされなくなるので、コレ、結構ひっかかるケースが多そうですね。

デメリットについて

基本的に、webサイト側での対応になるので、フィッシングサイト対応などはできないようです。 また、metaタグに書くので、ワンタイムコードが丸見えというのも指摘されそうですが、そもそも、nonce属性にコードを書いているので、伏せる必要もないし、サーバー側に記述して、修正や取り外しなどが簡易に行えなくするよりも、 metaタグをコメントアウトすると、手軽にデバッグできるようにしたほうが、サイト運用に向いていると思うんですよね。 先述しましたが、読み込み型の外部ライブラリサービスなどを使う時は、script読み込みが発生する場合は、おそらく殆どの場合が使えないし、nonce属性をつけてもらう仕様にしてもらうことは、おそらく不可能なんじゃないかと思うので、 この機能を入れて安心しないほうがいいでしょうね。 でも、xssが悩ましいのであれば、そもそも、そうした外部スクリプトを排除する意味でもnonceは有効になるのかもしれませんね。

余談

metaタグや、php-headerで"Content-Security-Policy"を出力する際に、nonce値をそのまま表記するのではなく、"nonce-"というプレフィックスを文字列の先頭につけないと、正常に動作しなかったので、この点少し時間を取られてしまいました。 あと、ワンタイムコードは、今回はbase64エンコード値を使っていますが、特に何の文字列であっても問題はないようです。 そして、今回javascriptのタグのみでやりましたが、このnonce属性は、imgタグや、css、など、HTMLのオブジェクト指向的なメディア要素や外部プログラムには、だいたい対応できるようです。 xssなどにお困りの場合のwebサイト対応にいかがでしょうか?

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。