Skip to main content

AngularJSセキュリティの基礎

著者
Cheat Sheet AngularJS

2020年3月17日

0 分で読めます

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

XSS、コードインジェクション、オープンリダイレクト、脆弱な依存関係への対策など、AngularJSのセキュリティに関する10のベストプラクティスをまとめたチートシート。

AngularJSセキュリティの基礎をダウンロード

このブログ記事では、次のAngularJSセキュリティのベストプラクティス10選を取り上げます。

  1. 「Angular流」の方法でXSSから保護する

  2. Angular DOM関連の入力インジェクションを避ける

  3. 信頼できないソースからAngularテンプレートを動的に読み込まない

  4. AngularJSのオープンリダイレクト脆弱性

  5. サーバーサイドAngularコードインジェクション

  6. DOM操作にAngularのjQuery互換API .elementを使わない

  7. Angular用セキュリティリンターを使う

  8. Angularのサードパーティーコンポーネントの脆弱性をスキャンして修正する

  9. Angularアプリケーションの組み込みCSRF対策

  10. Angularに組み込まれたCSPとの互換性

AngularJSのセキュリティのベストプラクティスが重要なのはなぜでしょうか?

以前実施したJavaScriptフレームワークのセキュリティに関する調査によると、Angular v1.xはAngularの全バージョンのダウンロード数の約30%を占め、2019年7月には200万件をわずかに超えるダウンロードがありました。

Angular v1.xのダウンロード数が約150万~200万件で推移し、新しいAngularのバージョンのダウンロード数が2018年6月から2019年7月にかけて300万件から500万件に増加したことを示す折れ線グラフ。

出典: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要素に表示します。

<div class="jumbotron" ng-controller="AboutController as about">
 <h1>About</h1>
 <p>
   Signed by:
 </p>
 <div ng-bind="about.signature"></div>
</div>

このビューに値を渡すコントローラーはAboutControllerです。

(function() {
 "use strict";
 angular.module("angularjs-starter").controller("AboutController", function() {
   this.signature =
     'Yours truly, Angle <img src="x" onError="alert('its me!')"/>> Bob';
 });
})();

ご覧のとおり、signatureの値にHTML要素を挿入し、ブラウザーのwindow alertメッセージを表示させようとする試みをシミュレートしています。しかし画面に表示される際、Angularは<などの文字をHTMLエンティティにエンコードします。そのため、実際のHTML要素としてDOMに挿入されるのではなく、テキストとして出力されます。

Aboutセクションに、HTMLエスケープ済みの署名メッセージとonerrorアラートのペイロードが表示された、AngularJSの出力エンコーディングの例。

Angularの二重中括弧を使う

HTML要素の値にデータをバインドするもう一つの方法は、Angularの規約である二重中括弧を使うことです。ngBindディレクティブと中括弧表記は同じ機能ですが、UXに関してわずかな違いがあります。

中括弧を使ったときにAngularが悪意あるデータをどのようにエンコードするか、まずビューにdescription値を追加する例で見てみましょう。

<div class="jumbotron" ng-controller="AboutController as about">
 <h1>About</h1>
 <p>
   {{ about.description }}
 </p>

 <p>
   Signed by:
 </p>
 <div ng-bind="about.signature"></div>
</div>

次に、クロスサイトスクリプティング攻撃をページに挿入しようとする悪意ある値を渡す、対応するコントローラーを示します。

(function() {
 "use strict";
 angular.module("angularjs-starter").controller("AboutController", function() {
   this.signature =
     'Yours truly, Angle <img src="x" onError="alert('its me!')"/>> Bob';
   this.description =
     "hello there you <a href="javascript:alert('hello')">crazy</a> diamond";
 });
})();

「Angular流」の方法に従っているため、中括弧とngBindディレクティブの両方で悪意のある可能性があるユーザー入力を扱うとき、Angularがクロスサイトスクリプティング攻撃への対策として出力エンコードを適用することがわかります。

Aboutセクションに、エスケープされたJavaScriptのリンクと画像のマークアップを表示する、AngularJSの出力エンコーディングとサニタイズの例

要点: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テンプレート:

<div ng-controller="AboutController" ng-app>
  <span>{{email}}</span>
  <div ng-bind-html-unsafe="link">
  </div>
</div>

ビューに対応するAngularコントローラー:

function AboutController($scope) {
  $scope.email = 'Email me:'
  $scope.link = "<a href='javascript:alert(1)'>Contact me</a>"
}

レンダリングされた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テンプレートは次のとおりです。

<html>
  <body ng-app='myApp'>
    <div ng-controller="AboutController">
      <span>{{email}}</span>
      <div ng-bind-html="link | stripDangerousHTML">
      </div>
    </div>
  </body>
</html>

Angularコントローラー:

var myApp = angular.module('myApp', []);

myApp.filter('stripDangerousHTML', function($sce) {
  return function(value) {
    let dataInput = value
    dataInput = dataInput
      .replace(/javascript/g, '')
      .replace(/alert/g, '');
    return $sce.trustAsHtml(dataInput);
  }
})

myApp.controller('AboutController', function($scope) {
  // this is filtered ok:
  // const emailAddress = 'javascript:alert(1)'
  // but is this??
  const emailAddress = `jav
ascript:al
ert(1);`
  $scope.email = 'Email me:'
  $scope.link = `Hi there, <a href="${emailAddress}">Contact me</a>`
});

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テンプレートは次のようなものです。

<div>
  Hello, {{ name }}
</div>

通常、Angularテンプレートは、ビルド時にAngularアプリケーションへ組み込まれるファイルで構成します。一方、実行時にクライアント側からリクエストを送信してテンプレートをダウンロードし、動的に評価することで、HTMLテンプレートを遅延読み込みすることもできます。

しかし、Webアプリケーションの制御や信頼の及ばないドメインからテンプレートを安全でない方法で読み込むリスクが生じます。また、中間者攻撃を軽減するため、最新バージョンのTLSなど、安全なプロトコルを使ってテンプレートにアクセスする必要があります。

Angularテンプレートをネットワーク経由で動的に読み込む例:

angular.module(‘myModule’)
  .component(‘myComponent’, {
     templateUrl: ‘/path/to/template.html’,
     controller: function() {}
   });

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コードを使っているかもしれません。

次の例を見てみましょう。

myApp.controller('AboutController', function($scope, $location) {
    $scope.submit = function() {
       const referer = $location.hash()
     }

     window.location.href = referer
});

この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コードインジェクションを防ぐためのベストプラクティス:

  1. ユーザー入力をそのまま使ったページ遷移は避ける。

  2. ユーザー入力に基づいてページ遷移する必要がある場合は、辞書マップを使って入力値とページまたはURLを対応付ける。

  3. ページ遷移用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テンプレートを考えてみましょう。

    <div ng-controller="AboutController">
      <span id=”content”>{{content}}</span>
    </div>

次は、angular.element()を使ってページを操作する、対応するAngularコントローラーです。

function AboutController($scope) {
  const element = angular.element(‘#content’)
  element.after($scope.content)
}

問題は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プラグインが問題を検出する例は次のとおりです。

/*eslint angular/no-jquery-angularelement: 2*/
// invalid
$(angular.element("#id")) // error: angular.element returns already a jQLite element. No need to wrap with the jQuery object

// invalid
jQuery(angular.element("#id")) // error: angular.element returns already a jQLite element. No need to wrap with the jQuery object

angular.element()の使用がセキュリティ上の問題となる理由がわからない場合は、前のセクション「DOM操作にAngularの.element jQuery互換APIを使用しない」をお読みください。

重要なポイント:静的コード解析ツールを使って、安全でないコードの検出を自動化し、開発の早い段階で問題が見つかったときに開発者へ通知しましょう。AngularJSのセキュリティの基本となるセキュリティリンター:

8. Angularのサードパーティコンポーネントの脆弱性を検出して修正する

Webアプリケーションの構築にあたり、Angularのコアや拡張コンポーネントに加えて、オープンソースパッケージを利用している可能性は高いでしょう。実際、AngularJSプロジェクト自体にも脆弱性があります。SnykによるJavaScriptフレームワークのセキュリティに関する調査では、AngularJSの1.x系全体で20件を超えるセキュリティ脆弱性が見つかっています。

Angular 1.xのバージョン別脆弱性数を示す棒グラフ:1.1は1件、1.2は4件、1.3は3件、1.4は1件、1.5は7件、1.6は3件。

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

AngularとReactの脆弱なモジュール、間接的な脆弱性、深刻度、年間ダウンロード数、修正可能性を比較した表。

脆弱性をスキャンする

AngularJSのセキュリティスキャナーをお探しですか?Snykを使い、GitHubまたはBitbucketのプロジェクトを接続すれば、AngularJSのセキュリティテストを実施し、フロントエンドプロジェクトの脆弱性を常に把握できます。Snykが自動的に脆弱性を検出し、修正用のプルリクエストを作成します。

AngularJSのセキュリティ問題を手早く特定するには、Snyk CLIを使う方法もあります。

npm install -g snyk
snyk test

Snyk testを実行すると、検出された脆弱性と脆弱な依存関係のパスが表示されます。これにより依存関係ツリーをたどり、どのモジュールが脆弱性を持ち込んだのかを把握できます。

npm auditを使っている場合、Snykが報告するAngularJSのセキュリティ脆弱性を見逃している可能性があります。npm auditはReactの3つの脆弱性もすべて見逃すため、開発者に誤った安心感を与えてしまいます。

ターミナルの出力に、Angular 1.2.32に重大度の高いクロスサイトスクリプティングの脆弱性があり、脆弱なパスが11件あると表示されています。
出典: https://snyk.io/blog/angular-vs-react-security-bakeoff-2019/

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のベストプラクティス:

  1. 「Angular流」の実装でXSSから保護する

  2. AngularのDOM関連の入力インジェクションを避ける

  3. 信頼できないソースからAngularテンプレートを動的に読み込まない

  4. AngularJSのオープンリダイレクト脆弱性

  5. サーバーサイドのAngularコードインジェクション

  6. DOM操作にAngularの.element jQuery互換APIを使用しない

  7. Angularのセキュリティリンターを使う

  8. Angularのサードパーティコンポーネントの脆弱性を検出して修正する

  9. Angularアプリケーションに組み込まれたCSRF対策

  10. Angularに組み込まれたCSP対応

AngularJSセキュリティのベストプラクティス・チートシートをダウンロード!

続きを読む

illustration hero ai
Blog

PHPが重大な未認証RCEの脆弱性2件を修正(CVE-2026-109618、CVE-2026-109619):今すぐアップデート

PHPのリクエスト処理に重大な脆弱性が2件あり、未認証のリモートコード実行につながるおそれがあります。修正版にアップグレードするか、旧バージョンにはベンダー提供のバックポートを適用してください。

blog feature ai lilac
Blog

エージェント型修正で脆弱性バックログをプルリクエストに変える

Evo Remediation Agentがオープンプレビューで利用可能になり、Evo Agentic AppSecに依存関係の自動解決機能が加わりました。AppSecチームは継続的な修正キャンペーンを実施できるようになりました

AI Background min crop
Blog

AIが、あなたが受け入れてきたあらゆるリスクを一気に顕在化させる:AI Security Panel: Unfilteredから得た7つの洞察

AIによって攻撃はより安価かつ高速になり、組織がすでに抱えているリスクが露呈しています。4人のセキュリティリーダーが、機械の速度に対応する防御と迅速な修復が今不可欠である理由を語ります。