AngularJSセキュリティの基礎
2020年3月17日
0 分で読めますこのAngularJSセキュリティのベストプラクティス・チートシートでは、AngularJSに焦点を当て、安全なコーディングを実現するためのヒントとガイドラインを紹介します。つまり、Web開発者向けにAngularJSセキュリティの基礎をまとめたチートシートです。

このブログ記事では、次のAngularJSセキュリティのベストプラクティス10選を取り上げます。
AngularJSのセキュリティのベストプラクティスが重要なのはなぜでしょうか?
以前実施したJavaScriptフレームワークのセキュリティに関する調査によると、Angular v1.xはAngularの全バージョンのダウンロード数の約30%を占め、2019年7月には200万件をわずかに超えるダウンロードがありました。

出典:https://snyk.io/blog/javascript-frameworks-security-report-2019/
AngularはAngularJSより優れているのでしょうか?
AngularはAngularJSの後継バージョンです。Angularの中核コンポーネントでは、報告されたセキュリティ脆弱性が見つかっていない点に注目してください。一方、AngularJSでは、バージョン1.xのすべてのリリースブランチを通じて23件の脆弱性が追跡されています。
どのバージョンのAngularJSを使うべきでしょうか?
常にAngularJSの最新バージョンを選びましょう。この記事の執筆時点ではAngularJS 1.7が最新です。最新バージョンにはセキュリティ修正が反映されており、2021年6月30日までサポートされる予定です。
AngularJSのセキュリティに関する広範な調査と有益な情報を以前共有してくださったLewis Ardenに感謝します。OWASP LondonでAngularJSのセキュリティについて講演した内容はこちらをご覧ください。
AngularJSのGitHub / Bitbucketプロジェクトのセキュリティ脆弱性をテストする
1. 「Angular流」の方法でXSSから保護する
AngularJSはデフォルトで、DOMに追加されるあらゆるデータ値に対し、コンテキストを考慮した出力エンコードと入力サニタイズを自動的に適用します。「Angular流」の方法に従っていれば、この保護機能を利用できます。
いくつか例を見てみましょう。
Angularのng-bindを使う
Angularのng-bindディレクティブを使うと、コントローラーなどのAngularコードで設定した値を、HTML要素の値にバインドできます。
次のview.htmlはAngularテンプレートです。ng-bindを使ってAngularコード内の式を展開し、その値をdiv HTML要素に表示します。
このビューに値を渡すコントローラーはAboutControllerです。
ご覧のとおり、signatureの値にHTML要素を挿入し、ブラウザーのwindow alertメッセージを表示させようとする試みをシミュレートしています。しかし画面に表示される際、Angularは<などの文字をHTMLエンティティにエンコードします。そのため、実際のHTML要素としてDOMに挿入されるのではなく、テキストとして出力されます。

Angularの二重中括弧を使う
HTML要素の値にデータをバインドするもう一つの方法は、Angularの規約である二重中括弧を使うことです。ngBindディレクティブと中括弧表記は同じ機能ですが、UXに関してわずかな違いがあります。
中括弧を使ったときにAngularが悪意あるデータをどのようにエンコードするか、まずビューにdescription値を追加する例で見てみましょう。
次に、クロスサイトスクリプティング攻撃をページに挿入しようとする悪意ある値を渡す、対応するコントローラーを示します。
「Angular流」の方法に従っているため、中括弧とngBindディレクティブの両方で悪意のある可能性があるユーザー入力を扱うとき、Angularがクロスサイトスクリプティング攻撃への対策として出力エンコードを適用することがわかります。

要点:Angularのデフォルトの保護機能を活用して、クロスサイトスクリプティング(XSS)脆弱性を軽減しましょう。ngBindディレクティブや{{ data }}のような中括弧を使い、「Angular流」にデータを展開すれば、Angularがデフォルトで出力エンコードとサニタイズを適用します。
2. Angular DOM関連の入力インジェクションを避ける
Angularの安全でないHTMLバインディングを悪用する
AngularJS 1.2以前などの旧バージョンにはng-bind-html-unsafeディレクティブがあり、HTMLデータをDOMに挿入して新しい要素を作成できました。この機能を使い、入力データから直接DOM要素を作成したうえで、リンクなど特定の文字列を削除する方法もありました。
このようなアプローチでデータをブラックリスト化する方法は、時間とともに新たな攻撃経路や手法が見つかり、ブラックリストの保守が必要になるため、間違いやすく、常に安全とは限りません。
ng-bind-html-unsafeがHTML要素をDOMに挿入する例を見てみましょう。
Angularテンプレート:
ビューに対応するAngularコントローラー:
レンダリングされたHTMLビューには「Contact me」が実際のリンクとして表示され、クリックするとalertが実行されます。

AngularのStrict Contextual Escapingを悪用する
AngularJS 1.2ではng-bind-html-unsafeディレクティブが非推奨となり、代わりにStrict Contextual Escaping(SCE)が導入されました。SCEは、特定のHTML要素のデータではなく、コンテキストに基づいてデータをエスケープし、サニタイズします。
ただし、エンコードを回避して、提供されたデータを安全なものとして扱える抜け道も存在します。Angularの$sce.trustAsHtml()や$sce.trustAs(type, value)メソッドを使うと、こうした操作が可能になります。
さらに、$sceProvider.enabled(false)を使うと、Strict Contextual Escapingを全体的に無効にし、すべてのサニタイズを無視して入力を信頼できます。これも避けるべき悪いプラクティスです。本当にやむを得ない理由がある場合に限って使い、他の対策や防御も必ず講じることをおすすめします。
ユーザーがリンクを作成できるようにする一方で、クロスサイトスクリプティング脆弱性を引き起こす例を見てみましょう。Angularテンプレートは次のとおりです。
Angularコントローラー:
stripDangerousHTMLというAngularフィルターを作成することで、開発者は悪意あるJavaScriptコードがhref属性に入り込まないようにしています。メールアドレスだけが使われるよう、サニタイズする想定です。
ユーザーが入力したemailAddressの値が'javascript:alert(1)'なら、正規表現に基づくパターンマッチングはうまく機能します。しかし、ユーザー入力がjav ascript:al ert(1);の場合、十分に機能しません。この入力はパターンマッチングを回避し、有効なJavaScript文として扱われるため、XSSを引き起こします。
要点:脆弱性を引き起こす可能性があるため、Angular DOM関連の入力インジェクションは避けましょう。
Angular 1.2以前では、
ng-bind-html-unsafeディレクティブを使わないでください。Angular 1.2以降では、
$sce.trustAsHtml(value)などのStrict Contextual Escapingメソッドを使って、ユーザー入力を無条件に信頼しないでください。
3. 信頼できないソースからAngularテンプレートを動的に読み込まない
Angularコードでは、常にAngularテンプレートを使用します。HTMLテンプレートは次のようなものです。
通常、Angularテンプレートは、ビルド時にAngularアプリケーションへ組み込まれるファイルで構成します。一方、実行時にクライアント側からリクエストを送信してテンプレートをダウンロードし、動的に評価することで、HTMLテンプレートを遅延読み込みすることもできます。
しかし、Webアプリケーションの制御や信頼の及ばないドメインからテンプレートを安全でない方法で読み込むリスクが生じます。また、中間者攻撃を軽減するため、最新バージョンのTLSなど、安全なプロトコルを使ってテンプレートにアクセスする必要があります。
Angularテンプレートをネットワーク経由で動的に読み込む例:
Angularではテンプレートリソースを制御できます。公式ドキュメントには次のように説明されています。
$sceDelegateProviderを使うと、AngularJSテンプレートやその他のスクリプト実行URL($sce.RESOURCE_URLコンテキストを使用するすべての場所)の取得元となるURLの安全性を確保するためのホワイトリストとブラックリストを取得・設定できます。
AngularアプリケーションのコードでSCE委譲プロバイダーを使う場合は、リソースのホワイトリスト設定を適切に検討してください。
リモートテンプレートの取得には安全な通信手段としてHTTPSを使い、リモートエンドポイントのTLS設定が最新であることを確認する。
ドメインやプロトコルに二重アスタリスクのワイルドカード**を使わない。
必要に応じて、多層防御のためにブラックリストを作成する。
4. AngularJSのオープンリダイレクト脆弱性
ブラウザー標準のAPIには、$window.locationなど、ページ遷移を行う機能があります。Angularでは$locationサービスプロバイダーを使って遷移処理を抽象化し、$location.url()、$location.path()、$location.hash()などのメソッドを提供します。これらを使って現在の遷移先URLにアクセスし、必要に応じて同期できます。
URLのクエリパラメーターやその他のURL関連データに基づいて遷移するAngularコードを使っているかもしれません。
次の例を見てみましょう。
このAngularコードは、たとえばhttps://www.example.com/path/to/this#refererのようなURLを解析し、別のページへ遷移する際のベースとして使うことを意図しています。
しかし、Webアプリケーションを閲覧するユーザーがURLアドレスを制御できるため、ハッシュに有効なJavaScriptコードを設定できます。たとえばhttps://www.example.com/path/to/this#javascript:alert(1) —とすれば、そのURLを実行できます。
同様に、window.location.hrefにユーザー入力を設定すると、たとえばハッシュを使ったJavaScriptインジェクションの例と同じ結果になります。
AngularJSセキュリティの基礎
オープンリダイレクトとJavaScriptコードインジェクションを防ぐためのベストプラクティス:
ユーザー入力をそのまま使ったページ遷移は避ける。
ユーザー入力に基づいてページ遷移する必要がある場合は、辞書マップを使って入力値とページまたはURLを対応付ける。
ページ遷移用URLにユーザー入力を連結しない。
要点:ユーザー入力をそのままページ遷移に使うことで発生する、オープンリダイレクトの問題を避けましょう。
JavaScriptコードインジェクション攻撃につながる可能性がある
window.location.href = $location.hashのようなパターンは避けてください。ユーザー入力に基づいてページ遷移する場合は、辞書マップを使いましょう。
5. サーバーサイドAngularコードインジェクション
サーバーサイドのコードはスクリプトなどの悪意あるHTMLエンティティをサニタイズまたはエンコードしますが、クライアント側のAngularJSコンテキストは認識しません。そのため、攻撃者が中括弧と追加のJavaScriptペイロードを含むAngularテンプレート式を提供すると、クライアント側の実行時に評価され、インジェクション攻撃につながる可能性があります。
サーバーサイドAngularコードインジェクションを軽減するためのベストプラクティス:
サーバーサイドとクライアントサイドのテンプレートを混在させないでください。テンプレートはサーバー側またはクライアント側のいずれか、一つのアプリケーションコンテキスト内だけで扱います。
ng-appディレクティブの適用範囲をHTMLドキュメントのbodyから、ページ内の特定のDOM要素に限定します。テンプレートのデータを
ng-bindまたはng-bind-htmlにバインドし、これらのAngularディレクティブが提供する出力エンコードとサニタイズ機能によって、ユーザー入力が適切に処理されるようにします。ng-non-bindableを使い、データがAngularによって評価対象の式として扱われないようにして、Angularコードインジェクションを軽減します。
要点:サーバーサイドAngularコードインジェクションを軽減しましょう。
6. DOM操作にAngularの.element jQuery互換APIを使用しない
Angularには、DOMを直接検索・操作するためのjQueryライクなAPI互換性(jqLite)を提供するangular.element() APIがあります。
DOMへのアクセスや操作に便利な方法に思えるかもしれませんが、安全でないDOMインジェクションによって、インジェクション脆弱性が生じる可能性があります。
次のようなシンプルなAngularテンプレートを考えてみましょう。
次は、angular.element()を使ってページを操作する、対応するAngularコントローラーです。
問題はelement.after()にあります。これは、jQuery独自のafter APIとの互換性を保つため、エンコードやサニタイズを行わずに$scope.contentの内容をDOMに追加します。この変数に悪意のある内容が含まれていると、Angularの保護機能が適用されず、XSSの被害を受ける可能性があります。
Angular公式ドキュメントのhttps://docs.angularjs.org/api/ng/function/angular.elementで、angular.elementの詳細をご覧ください。
重要なポイント:Angularのangular.element() jQuery互換APIを使ったDOM操作は避けましょう。DOMにHTML要素を直接作成することで、クロスサイトスクリプティング脆弱性につながる可能性があります。
7. Angularのセキュリティリンターを使う
JavaScript開発ではリンターが広く使われており、開発者はESLintのような人気プロジェクトを、機能を拡張するプラグインと組み合わせて利用することがよくあります。eslint-plugin-angularは、Angularの一般的なコーディング規約やガイドラインに役立ちます。セキュリティ関連のルールも含まれており、その1つであるno-jquery-angularelementは、angular.elementオブジェクトをjQueryまたは$.でラップすることを禁止します。
公式ドキュメントによると、ESLintプラグインが問題を検出する例は次のとおりです。
angular.element()の使用がセキュリティ上の問題となる理由がわからない場合は、前のセクション「DOM操作にAngularの.element jQuery互換APIを使用しない」をお読みください。
重要なポイント:静的コード解析ツールを使って、安全でないコードの検出を自動化し、開発の早い段階で問題が見つかったときに開発者へ通知しましょう。AngularJSのセキュリティの基本となるセキュリティリンター:
eslint-plugin-scanjs-rules
eslint-plugin-angular angularのangular.element()の使用を禁止
8. Angularのサードパーティコンポーネントの脆弱性を検出して修正する
Webアプリケーションの構築にあたり、Angularのコアや拡張コンポーネントに加えて、オープンソースパッケージを利用している可能性は高いでしょう。実際、AngularJSプロジェクト自体にも脆弱性があります。SnykによるJavaScriptフレームワークのセキュリティに関する調査では、AngularJSの1.x系全体で20件を超えるセキュリティ脆弱性が見つかっています。

さらに、AngularベースのWebアプリケーションの構築に使う依存関係にも、セキュリティ脆弱性が含まれている可能性があります。次の表に示すように、脆弱性によっては、現時点で修正方法が提供されていないものもあります。

脆弱性をスキャンする
AngularJSのセキュリティスキャナーをお探しですか?Snykを使い、GitHubまたはBitbucketのプロジェクトを接続すれば、AngularJSのセキュリティテストを実施し、フロントエンドプロジェクトの脆弱性を常に把握できます。Snykが自動的に脆弱性を検出し、修正用のプルリクエストを作成します。
AngularJSのセキュリティ問題を手早く特定するには、Snyk CLIを使う方法もあります。
Snyk testを実行すると、検出された脆弱性と脆弱な依存関係のパスが表示されます。これにより依存関係ツリーをたどり、どのモジュールが脆弱性を持ち込んだのかを把握できます。
npm auditを使っている場合、Snykが報告するAngularJSのセキュリティ脆弱性を見逃している可能性があります。npm auditはReactの3つの脆弱性もすべて見逃すため、開発者に誤った安心感を与えてしまいます。

Snykは、修正版へのアップグレードに向けた具体的な修正アドバイスを提供し、リポジトリに自動でプルリクエストを作成します。修正が提供されていない場合は、Snykが提供するパッチを適用して脆弱性を緩和することもできます。
AngularJSのセキュリティスキャナーに近いものをお探しなら、オープンソースの依存関係を追跡し、脆弱性の通知を受け取り、脆弱性が見つかったら修正できるSnykから始めましょう。
重要なポイント:AngularJSにはこれまでに20件を超える脆弱性があり、ダウンロード数が数百万件に達するAngularコンポーネントにも、依然として脆弱性が残っています。
Snykを接続してGitHubなどのSCMと連携し、プロジェクトに最適なCI/CD統合を実現しましょう。
Snykは、使用しているサードパーティコンポーネントの脆弱性を検出し、修正用プルリクエストを作成します。バージョン更新をマージして、リスクを軽減できます。
9. Angularアプリケーションに組み込まれたCSRF対策
クロスサイトリクエストフォージェリ(CSRF、XSRF)は、フォームを送信するAPIリクエストなど、ユーザーの意図しない操作を実行するように仕向けるWebセキュリティ攻撃です。
CSRF脆弱性を緩和する方法の1つは、ランダムに生成したトークンを使うことです。さらに望ましいのは、リクエストが信頼できることを認証する手段として、暗号学的なノンスを使用することです。
AngularにはCSRFトークンの組み込みサポートがあり、サーバーから送信されるXSRF-TOKENなど、特定の命名規則に従ったCookieを読み取り、その値をAngularアプリケーションから送信する後続のHTTPリクエストに追加します。
AngularJSでは、CSRFトークンの処理はすべて$httpサービス内で行われます。新しいAngularバージョンでは、公式サポート対象の@angular/common/httpモジュールに含まれるHttpClientからも利用できます。また、$http.xsrfCookieName APIなどを使って、CSRFトークンのCookie名や全般的な動作を設定できます。
重要なポイント:Angularには、クライアント側で$httpサービスを介してCSRFトークンを処理する組み込みサポートがあります。独自に実装せず、このサービスを使いましょう。
10. Angularに組み込まれたCSP対応
インラインJavaScriptコードの評価を禁止するなど、一般的なCSP制限と競合するAngularの機能もあります。しかし、推奨されないCSPの全面的な無効化ではなく、AngularのngCspディレクティブを使えば、CSP設定との互換性を確保できます。
コンテンツセキュリティポリシー(CSP)は、信頼ポリシーを設定できるWeb標準です。主にセキュリティを強化する多層防御として利用され、XSSインジェクションなどの問題の緩和に役立ちます。
AngularJSにはCSP関連の組み込み制御がありますが、有効にすると一部の機能が制限されます。たとえば、eval()が許可されないため、インラインスクリプトや式は使えません。一方で、AngularJSの一部機能では内部的にこれらが必要です。
CSPについては、MozillaのWebサイトにあるおすすめの解説と、Angular APIドキュメントのngCspディレクティブをご覧ください。
重要なポイント:コンテンツセキュリティポリシー(CSP)の導入は、セキュリティをさらに強化する重要なステップです。特に、クロスサイトスクリプティング攻撃の緩和に役立ちます。
AngularJSセキュリティの基本となる10のベストプラクティス:



