Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
AngularJS TIPS

AngularJS TIPS

コンテンツ・セキュリティ・ポリシーを利用する(ng-csp)

2016年4月25日

セキュリティフレームワーク「CSP」による制限ポリシーを有効にした場合に、AngularJSでは特定のケースでエラーとなる。そのケースの内容と回避方法を解説する。

  • このエントリーをはてなブックマークに追加

 コンテンツ・セキュリティ・ポシリー(Content Security Policy:CSP)とは、クロスサイト・スクリプティング(XSS)に代表される、よくあるアプリへの攻撃を軽減するためのセキュリティフレームワークです。あらかじめ設定したポリシーによって、ブラウザー(アプリ)に制限を加えることで、アプリの安全性を高めます。制限できる主な事項には、以下のようなものがあります。

  • JavaScript疑似プロトコル(href="javascript:~")の禁止
  • インラインのスクリプト/スタイルシートの禁止
  • インライン・イベントハンドラー(onclickなど)の禁止
  • eval関数をはじめ、Functionコンストラクター、setTimeoutsetInterval関数などへの文字列コードの禁止
  • 異なるドメインからのスクリプト/スタイルシートのインポートを禁止
  • ブラウザープラグイン(Flash、Javaなど)の利用を禁止

 これらの制限ポリシーは、レスポンスヘッダーによってブラウザーに通知され、ブラウザーの挙動を制限することになります。例えばPHPで、ポリシー設定のためのヘッダーを出力するには、以下のようにします。もちろん、理屈が分かっていれば、他の言語でも同じように記述できます。

PHP
<?php
header("Content-Security-Policy: default-src 'self' ajax.googleapis.com");
?>
CSPを有効化するコードの例

 CSPを有効化するのは、Content-Security-Policyヘッダーの役割です。ヘッダー値にはさまざまなポリシー情報を指定できますが、まずここでは、現在のホスト(self)とajax.googleapis.comへの参照だけを許可していると理解しておいてください。Content-Security-Policyヘッダーの詳細については、「CSP (Content Security Policy) - MDN」などの資料を参照することをお勧めします。

AngularJSとCSP

 さて、まずはCSPを有効にした状態で、AngularJSアプリを動作してみましょう。

PHP
<?php
header("Content-Security-Policy: default-src 'self' ajax.googleapis.com");
?>
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="UTF-8" />
<title>AngularTips</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js"></script>
<script src="scripts/csp.js"></script>
</head>
<body ng-controller="MyController">
<div ng-cloak>{{1 + 3}}</div>
</body>
</html>
CSPを有効にした場合のAngularJSアプリ(csp.php)
JavaScript
angular.module('myApp', [])
 .controller('MyController', ['$scope', function($scope) {
 }]);
CSPを有効にした場合のAngularJSアプリ(csp.js)
コンソール
angular.js:3543 Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'self' ajax.googleapis.com". Either the 'unsafe-inline' keyword, a hash ('sha256-1PxuDsPyGK6n+LZsMv0gG4lMX3i3XigG6h0CzPIjwrE='), or a nonce ('nonce-...') is required to enable inline execution. Note also that 'style-src' was not explicitly set, so 'default-src' is used as a fallback.
実行時エラーが発生(開発者ツールから確認)

 AngularJSでは、もともとがCSPを意識した行儀のよいコードを提供していますが、以下のケースで例外的にCSPの制約に抵触します。

  • パフォーマンス最適化のためにFunctionコンストラクターを使用
  • インラインのスタイルシートを出力(ng-cloakディレクティブ)

 結果、上のようなエラーが発生しているわけです。これを解消するのがng-cspディレクティブの役割です。

ng-cspディレクティブの基本と注意点

 ng-cspデイレクティブ(属性)は、ルート要素に付与するだけです。

PHP
<?php
header("Content-Security-Policy: default-src 'self' ajax.googleapis.com");
?>
<!DOCTYPE html>
<html ng-app="myApp" ng-csp>
ng-cspディレクティブを付与したコード(csp.php)
エラーが出ずに正しく実行された(=コンソールにエラーが表示されない)
エラーが出ずに正しく実行された(=コンソールにエラーが表示されない)

 果たして、先ほどのエラーが出力されなくなったことが確認できます。

 このように、ng-cspディレクティブの使い方は誤解のしようもないものですが、注意すべき点もあります。

1パフォーマンスはやや低下する

 最適化のために使われていたFunctionコンストラクターなどが無効化されますので、いくらかパフォーマンスが低下する可能性があります。ただし、パフォーマンスに影響するコード(Angular式)は限定されていますので、さほど気にする必要はないでしょう。

2アプリではCSPの制約を守る必要がある

 ng-cspディレクティブは、あくまでAngularJS本体をCSPに即したコードで動作するための仕組みです(=CSPの制約を取り払っているわけではありません)。よって、アプリを実装する際には、本稿冒頭で触れたような制約を意識しなければなりません。

3インライン・スタイルシートは明示的にインポートする

 ng-cspディレクティブによって、それまで生成されていたインライン・スタイルシートはインポートされなくなります。よって、ng-cloakディレクティブを利用するに際しては、以下のように明示的にスタイルシートをインポートしてください。

HTML
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular-csp.css" />
ng-cloakディレクティブのためのスタイルシートを明示的にインポート

 

処理対象:コンテンツ・セキュリティ・ポリシー カテゴリ:ディレクティブ
API:ngCsp(ng-csp)|ngCloak(ng-cloak) カテゴリ:ng(コアモジュール) > directive(ディレクティブ)

※以下では、本稿の前後を合わせて5回分(第52回~第56回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

AngularJS TIPS
52. ルーティングの挙動/設定をカスタマイズするには?($routeProviderプロバイダー)

「テンプレートを文字列で指定(templateパラメーター)」「リダイレクト時の規則をカスタマイズ(redirectToパラメーター)」「html5モードに切り替える」という、特によく使われる3つのカスタマイズ方法を取り上げる。

AngularJS TIPS
53. 配列/オブジェクトをコピーするには?(copy)

配列のコピーで、JavaScript標準のconcatメソッドを使う場合とAngularJSのcopyメソッドを使う場合の違いを説明。シャローコピーとディープコピーとは?

AngularJS TIPS
54. 【現在、表示中】≫ コンテンツ・セキュリティ・ポリシーを利用する(ng-csp)

セキュリティフレームワーク「CSP」による制限ポリシーを有効にした場合に、AngularJSでは特定のケースでエラーとなる。そのケースの内容と回避方法を解説する。

AngularJS TIPS
55. 複数のオブジェクトを結合するには?(extend/merge)

angular.extendメソッドを利用して、既存の複数のオブジェクトを結合する方法と注意事項を解説。また、入れ子になったオブジェクトを再帰的にマージする方法も説明する。

AngularJS TIPS
56. AngularJSの管理外でサービスを注入するには?($injector)

AngularJSの管理外でも、$injectorサービスを使ってサービスを手動でインスタンス化することで、AngularJSが提供するサービスを利用できる。その基本的な利用方法を説明する。

サイトからのお知らせ

Twitterでつぶやこう!