$sce

概要

$sceは、Strict Contextual EscapingをAngularJSに提供するサービスです。

Strict Contextual Escaping

Strict Contextual Escaping (SCE)は、AngularJSがそのコンテキストを使用するのに、 安全であると確立されたコンテキストを紐付けることを必須とするモードです。(翻訳に自信なし) そのようなコンテキストの例の1つに、ng-bind-htmlを通したユーザーによって制御される任意のHTMLの紐付けがあります。 これらのコンテキストを特権があるもの、またはSCEコンテキストとして参照します。

Angularのバージョン1.2から、デフォルトでSCEが有効になります。

注意: 有効になっている場合、IE8のQuirksモードではサポートされません。 このモードでは、IE8は任意のJavaScriptを「式()」文を使用することで実行することを許可してしまします。 詳細については、http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspxを参照してください。 HTMLドキュメントのトップに<doctype html>を追加することで、 ドキュメントが標準モードであり、Quirksモードでは無いことを確認することが出来ます。

SCEはデフォルトで、コードがセキュアな方法で書かれる手助けをし、(a) XSSやクリックジャッキングのようなセキュリティ脆弱性の検査を行います。(b) これは、非常に簡単に行うことが出来ます。

下記は特権コンテキストの紐付けの例になります。

<input ng-model="userHtml">
<div ng-bind-html="{{userHtml}}">

ng-bind-htmlは、ユーザーによって制御されるように{{userHtml}}に紐付けられている事に注目してください。 SCEを無効にすると、このアプリケーションは任意のHTMLをDIV内に描画することを許可します。 より現実な例ですと、紐付けを通してのブログ記事などのユーザーコメントなどの描画があるかもしれません。 (HTMLは、ユーザー入力によって作られるセキュリティ脆弱性の、描画コンテキストの1つの例に過ぎません。)

HTMLのケースでは、クライアントサイド上、またはサーバサイド上のどちらかで、 値の紐付けとドキュメント内の描画の前に、安全ではないHTMLのサニタイズにライブラリを使用するかもしれません。

どのようにして各場所で、これらのような形式の紐付けを、ライブラリを使用されて値がサニタイズされていることを保証しているのでしょうか? (または、サーバによって安全である描画として返しているのでしょうか?) どのようにして、誤ってサニタイズされた行を削除していない事、 またはあるプロパティ/フィールドの名前の変更と紐付けられているサニタイズされた値の更新を忘れていないことを保証できるのでしょうか?

デフォルトで安全であるために、「そのコンテキスト内で紐付けして使用する値が安全である」と判断できない限り、 そのような紐付けが禁止されていることを確認したいはずです。 そのため、サーバから受け取った、ライブラリでサニタイズされた等の理由で、 値が安全であるという事を簡単に伝えるための検査(簡単なものであればgrepを使って)することが可能です。(翻訳に自信なし) これを使用することで、特定のディレクトリ内のファイルのみを許可するといったことも可能になります。(翻訳に自信なし) 任意の値を使用しないコードによる安全な内部APIであることを保証すれば、作業はより管理しやすくなります。(翻訳に自信なし)

AngularJSのSCEサービスの場合、SCE/特権コンテキストによって受け入れられる値を取得するために、 $sec.trustAs(と、$sce.trustAsHtmlのような略記メソッド等)を使用します。

どのように動作するのか?

特権コンテキスト内のディレクティブとコードは、値を直接紐付けるのでは無く、 $sce.getTrusted(context, value)の結果を紐付けます。 ディレクティブは、属性の紐付けの監視に$parseでは無く、$sce.parseAsを使用し、 $sce.getTrustedの背後の定数では無いリテラル上(?)で実行されます。(翻訳に自信なし)

例としてngBindHtmlは、$sce.parseAsHtml(紐付けの式)を使用します。 下記は実際のコードです。(多少、簡素化しています)

var ngBindHtmlDirective = ['$sce', function($sce) {
  return function(scope, element, attr) {
    scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
      element.html(value || '');
    });
  };
}];

テンプレート読み込みの影響

これは、ディレクティブによって指定されたtemplateUrlの物はもちろんのこと、 ng-includeディレクティブの両方に適用されます。

デフォルトで、Angularは同じドメインとプロトコルのテンプレートのみを、アプリケーションドキュメントとして読み込みます。 これは、$sce.getTrustedResourceUrlにテンプレートURLを指定して呼び出すことによって行われます。 他のドメインまたはプロトコル、もしくはその両方から、テンプレートを読み込むために、 ホワイトリストに加えるか、信頼出来る値としてラップするかの、どちらかを行うことが可能です。

注意: ブラウザのSame Originポリシーと、 Cross-Origin Resource Sharing(CORS)ポリシーは、 これに付加される形で適用され、テンプレート読み込み成功の可否がより制限されるかもしれません。(翻訳に自信なし) これは、正しいCORSポリシー無しでは、異なるドメインからのテンプレート読み込みが、全てのブラウザ上で動作しない事を意味します。 また、file://形式のURLからのテンプレート読み込みも、ブラウザによっては動作しません。

SCEは開発者への負担が大きいのでは?

SCEは、補足するための式にのみ、適用されるという事を覚えておくことは重要です。

もし、式が定数リテラルの場合、それらは自動的に信頼出来るものとされ、それらに対して$sce.trustAsを呼び出す必要はありません。 (例: <div ng-html-bind-unsafe="'<b>信頼できる値<b&bt;'"><div>)

$sceDelegateが含まれると、デフォルトでSCEについての認識無しに、 アプリケーションのドメインからng-include内のテンプレート読み込みを許可します。(翻訳に自信なし) これは、異なるドメインからのテンプレート読み込み、またはhttpsからhttpを介してのテンプレート読み込みをブロックします。 ホワイトリストとブラックリストにURLを指定することで、これらの設定を変更することが可能です。

これは余計なオーバーヘッドを減らすために重要になります。 オーバーヘッドを少なくし、アプリケーションをセキュアにし、正当な値であるかの検査を行うために、 後からアプリケーション上でセキュリティチェックを行うよりも、この方法の方がはるかに容易です。

信頼出来るとして指定可能なコンテキストのタイプは?

コンテキスト 説明
$sce.HTML アプリケーション内で安全なソースであるとするHTMLです。 ngBindHtmlディレクティブは紐付けに、このコンテキストを使用します。
$sce.CSS アプリケーション内で安全なソースであるとするCSSです。 現在、Angular内では使用されていません。 ディレクティブを作成する際に必要に応じて使用してください。
$sce.URL リンクとしても安全であるとするURLです。 現在、Angular内では使用されていません。 (<a href=と<img src= は、URLをサニタイズし、SECコンテキストを構成しません)
$sce.RESOURCE_URL

リンクとしては安全では無いが、URL先のコンテンツをアプリケーション内に含めても安全であるとするURLです。 例えば、ng-include、src/ngSrcを含んで紐付けを行うIMGタグ以外のものです。(例: IFRAME、OBJECT等)

$sce.RESOURCE_URLは、$sce.URLより強い立場にあるため、$sce.RESOURCE_URLが必要とする信頼出来る値であるコンテキストは、 $sce.URLで必要とされる信頼出来る値であれば、何処にでも使用することが可能です。(翻訳に自信なし)

$sce.JS アプリケーションのコンテキスト内で実行しても安全であるとするJavaScriptです。 現在、Angular内では使用されていません。 ディレクティブを作成する際に必要に応じて使用してください。

resourceUrlWhitelist / Blacklistのフォーマットについて

これらの配列の各要素には、下記のいずれかが必要です。

'self'
特別な文字列である'self'は、同じプロトコルを使用してアプリケーションドキュメントと、 同じドメインの全てのURLに対して一致させる場合に使用することが出来ます。
文字列 (特別な値である、'self'は除く)
  • 検査済みのリソースの、完全正規化/絶対URLを照合する文字列です。 (部分文字列の一致は、不十分。)
  • 2つのワイルドカード、***が存在します。 他の全ての文字列は、これらに一致します。
  • *: :/.?&;の6つの文字以外の、 任意の文字の0回以上の繰り返しに一致します。 これは、ホワイトリストで使用する際に便利なワイルドカードです。
  • **: 任意の文字の0回以上の繰り返しに一致します。 あまりにも多くのものに一致し過ぎてしまうため、スキーム、ドメイン等に使用するのは適切ではありません。
    (例: http://**.example.com/は、その意図は無かったとしても、http://evil.com/?ignore=.example.com/に一致してしまいます。) パスの末端で使用することをお勧めします。
    (例: http://foo.example.com/templates/**)
正規表現
  • 注意: 正規表現は強力且つ非常に柔軟である一方、その文法(と、それに必要なエスケープ)を保守する事を困難にします。 複雑な正規表現を更新しようとする際に、誤ってバグを生み出してしまうことが容易に考えられます。 (私見ですが、全ての正規表現は良質なテスト・カバレッジを持つべきです) 例えば、.の使用は、少数にのみ限定することが望ましいです。(翻訳に自信なし) 正規表現内の.文字列を使用して、スキームまたはサブドメインを一致させる場合、 意図せずにその文字列中の:または.に対して一致してしまいます。 通常は文字列のパータンを使用するようにして、最後の手段として正規表現を使用することを強くお勧めします。
  • 正規表現は、RegExpのインスタンスで無ければいけません(文字列は不可)。 これは検査済みのリソースの正規化/絶対URL全体に対して、一致されます。 (RegExpが^$を持たないとしても) 加えて、RegExpに提供されるフラグ(複数、グローバル、大文字小文字を区別しない、のような)は無視されます。
  • もし、他のテンプレートエンジン(お勧めは出来ません、issueの#4006参照)から、JavaScriptを生成する場合、 自分の正規表現をエスケープすることを忘れないでください。 (更に、テンプレートエンジンと値の補完処理に依存した、もう一段階のエスケープが必要かもしれないことにも注意してください。) コーディング以前に、プラットフォームに応じたエスケープ処理をすれば十分かもしれません。 例えば、RubyであればRegexp.escape(str)、Pythonであれば、re.escapeを持ちます。 JavaScriptのエスケープ関数には、同様の機能が不足しています。 Googleのクロージャライブラリのgoog.string.regExpEscape(s)を確認してみましょう。

例は、$sceDelegateProviderを参照してください。 SCEを使用した例が紹介されています。

getTrusted(type, maybeTrusted)

$sceDelegate.getTrustedへ委譲します。 そのため、$sce.trustAs()を呼び出し、もし、問い合されたコンテキストタイプが作成されたタイプのスーパータイプ(supertype)であった場合、 元の提供された値が返りその結果を取得します。 もしこの条件が満たされない場合、例外がスローされます。

引数 説明
type

型:string

この値が使用されているコンテキストの種類を指定します。

maybeTrusted

型:*

直前の$sce.trustAs呼び出しの結果を指定します。

戻り値 説明
 

型:* 初期値:3

もし、このコンテキスト内で有効である場合、$sce.trustAsに元々提供されていた値が返ります。 そうでなければ例外がスローされます。

 Back to top

© 2017 Google
Licensed under the Creative Commons Attribution License 3.0.

このページは、ページトップのリンク先のAngularJS公式ドキュメント内のページを翻訳した内容を基に構成されています。 下記の項目を確認し、必要に応じて公式のドキュメントをご確認ください。 もし、誤訳などの間違いを見つけましたら、 @tomofまで教えていただければ幸いです。

  • AngularJSの更新頻度が高いため、元のコンテンツと比べてドキュメントの情報が古くなっている可能性があります。
  • "訳注:"などの断わりを入れた上で、日本人向けの情報やより分かり易くするための追記を行っている事があります。