takuti.me ABOUT

2013-11-15

AngularJSでChromeExtension開発をするならCSPに気をつけよう

初めてのChromeExtensionを作った後にAngularJSに触れて、最初は「ChromeExtensionをAngularJS使って作ることができたらChromeの右上ライフが捗る!?」なんて思って試したわけですが、何故かAngularJSが動かず諦めていました。

それを最近ふと思い出したので調べてみたら、ChromeExtensionのCSP (Content Security Policy) が原因だったことがあっさりと分かって解決したのでメモ。

結論

AngularJSでChromeExtension開発をするためにやるべきことは以下の2つ。

  1. ng-appの横にng-cspと書いてあげる
  2. angular-csp.cssをローカルに持ってきてロードしてあげる

以下詳細。

CSP (Content Security Policy) とは

すごーくざっくりと言えば、XSSをはじめとする攻撃に利用されそうな機能に対して与える制限のこと。

そのような機能の分かりやすい例としてはeval()でしょう。ご存知の通りevalは与えられた文字列をソースとしてなんでもかんでも解釈・実行してしまうため、脆弱性があった際に与える影響は絶大です。

evalを使おう!とあなたが思った時、それは他の機能で代替可能ですので思い止まりましょう。というわけで、CSPでeval相当の記述を全て禁止にすることができたりするわけです。

ChromeExtensionのCSPとAngularJSの使い方

ChromeExtensionでは、CSPがデフォルトで設定されています。
Content Security Policy (CSP) - Google Chrome

設定されている制限の内容は、

  • evalとそれに相当する記述の無効化
  • インライン(HTMLに埋め込む形)でのJavaScript実行の禁止
  • スクリプトファイルやその他リソースファイルの読み込みはローカルからのみ

の3つ。

AngularJSでは実行速度向上のためにeval相当の記述を利用している箇所があり、そこがChromeExtensionのCSPに引っかかっているのです。10041行目(angular.js v1.2.0)から定義されているgetterFn関数です。これがChromeExtensionでAngularJSが動かなかった原因。

そして、この問題への対処法はAngularJS公式で用意されています。やり方はng-appと一緒にng-cspと書いてあげるだけ

<html lang="ja" ng-app ng-csp>

これで、AngularJSが実行速度向上を諦めるモードになります。そんなわけですべての式の評価にかかる速度が30%減になってしまうようですが、仕方ない。

しかしどうやら30%の速度だけでは等価交換にならなかったようで、CSPによって影響を受けたままのものがまだあります。それはCSSの一部。

angular.js(v1.2.0)の最下部では、AngularJSの導入に伴って必要となるCSSをまとめてjQueryでhead要素の先頭に埋め込んでいます。これは例えばバリデーション時に表示するエラーメッセージのhidden制御なんかが関係してくる。

その処理がChromeExtensionのCSPの1つ「インラインでのJavaScript実行の禁止」に引っかかってしまい、必要なCSSが埋め込まれない問題が発生します。実際、ng-cspを付けるだけだとバリデーションの結果に関係なくエラーメッセージが表示され続けます。

これを解決するには、jQueryを使って埋め込んでいたCSSを全て1つのCSSファイルにまとめてローカルからロードしてあげる必要があるのです。

ロードすべきCSSファイルは公式でも公開しています。
angular-csp.css

ここまでやって初めて、ChromeExtension上でAngularJSが問題なく動くようになります。(ぱちぱち

へんかんくん v0.2.0 が生まれました

以上、AngularJSでChromeExtension開発をした時のメモでした。

僕が何をしたかったかと言うと、初めて作ったChromeExtensionのリニューアルです。

num
takuti/conversion_crx

HTMLエスケープ機能が追加されて、名前は「しんすうへんかんくん」から「へんかんくん」になりました。僕のChromeの右上ライフが捗ります。それだけ。

参考

Making Chrome Extension with AngularJS from Ben Lau