# Angular ## チェックリスト チェックリスト [こちらから](https://lsgeurope.com/post/angular-security-checklist)。 * [ ] Angularはクライアントサイドのフレームワークと見なされ、サーバーサイドの保護を提供することは期待されていません * [ ] スクリプトのソースマップはプロジェクト設定で無効になっています * [ ] 信頼できないユーザー入力は常にテンプレートで使用される前に補間またはサニタイズされます * [ ] ユーザーはサーバーサイドまたはクライアントサイドのテンプレートを制御できません * [ ] 信頼できないユーザー入力は、アプリケーションによって信頼される前に適切なセキュリティコンテキストを使用してサニタイズされます * [ ] `BypassSecurity*` メソッドは信頼できない入力と共に使用されません * [ ] 信頼できないユーザー入力は、`ElementRef`、`Renderer2`、`Document`などのAngularクラスや他のJQuery/DOMシンクに渡されません ## Angularとは Angularは**強力**で**オープンソース**のフロントエンドフレームワークで、**Google**によって維持されています。**TypeScript**を使用してコードの可読性とデバッグを向上させます。強力なセキュリティメカニズムにより、Angularは**XSS**や**オープンリダイレクト**などの一般的なクライアントサイドの脆弱性を防ぎます。また、**サーバーサイド**でも使用できるため、**両方の視点**からのセキュリティ考慮が重要です。 ## フレームワークアーキテクチャ Angularの基本をよりよく理解するために、その基本的な概念を見ていきましょう。 一般的なAngularプロジェクトは通常次のようになります: ```bash my-workspace/ ├── ... #workspace-wide configuration files ├── src │ ├── app │ │ ├── app.module.ts #defines the root module, that tells Angular how to assemble the application │ │ ├── app.component.ts #defines the logic for the application's root component │ │ ├── app.component.html #defines the HTML template associated with the root component │ │ ├── app.component.css #defines the base CSS stylesheet for the root component │ │ ├── app.component.spec.ts #defines a unit test for the root component │ │ └── app-routing.module.ts #provides routing capability for the application │ ├── lib │ │ └── src #library-specific configuration files │ ├── index.html #main HTML page, where the component will be rendered in │ └── ... #application-specific configuration files ├── angular.json #provides workspace-wide and project-specific configuration defaults └── tsconfig.json #provides the base TypeScript configuration for projects in the workspace ``` ドキュメントによると、すべてのAngularアプリケーションには、コンポーネント階層をDOMに接続するルートコンポーネント(`AppComponent`)が少なくとも1つあります。各コンポーネントは、アプリケーションデータとロジックを含むクラスを定義し、ターゲット環境に表示されるビューを定義するHTMLテンプレートに関連付けられています。`@Component()`デコレーターは、その直下のクラスをコンポーネントとして識別し、テンプレートおよび関連するコンポーネント固有のメタデータを提供します。`AppComponent`は`app.component.ts`ファイルで定義されています。 Angular NgModulesは、アプリケーションドメイン、ワークフロー、または密接に関連する機能セットに専念したコンポーネントのセットのコンパイルコンテキストを宣言します。すべてのAngularアプリケーションには、通常`AppModule`と呼ばれるルートモジュールがあり、アプリケーションを起動するブートストラップメカニズムを提供します。アプリケーションには通常、多くの機能モジュールが含まれています。`AppModule`は`app.module.ts`ファイルで定義されています。 Angular `Router` NgModuleは、アプリケーション内の異なるアプリケーション状態とビュー階層の間でナビゲーションパスを定義できるサービスを提供します。`RouterModule`は`app-routing.module.ts`ファイルで定義されています。 特定のビューに関連付けられていないデータやロジックを共有したい場合は、サービスクラスを作成します。サービスクラスの定義は、`@Injectable()`デコレーターによって直前に示されます。このデコレーターは、他のプロバイダーを依存関係としてクラスに注入できるようにするメタデータを提供します。依存性注入(DI)により、コンポーネントクラスをスリムで効率的に保つことができます。これらはサーバーからデータを取得したり、ユーザー入力を検証したり、コンソールに直接ログを記録したりすることはなく、そのようなタスクをサービスに委任します。 ## Sourcemap configuration Angularフレームワークは、`tsconfig.json`オプションに従ってTypeScriptファイルをJavaScriptコードに変換し、その後`angular.json`構成でプロジェクトをビルドします。`angular.json`ファイルを見てみると、ソースマップを有効または無効にするオプションがあることがわかりました。Angularのドキュメントによると、デフォルトの構成では、スクリプト用のソースマップファイルが有効になっており、デフォルトでは隠されていません。 ```json "sourceMap": { "scripts": true, "styles": true, "vendor": false, "hidden": false } ``` 一般的に、sourcemapファイルはデバッグ目的で使用され、生成されたファイルを元のファイルにマッピングします。したがって、プロダクション環境での使用は推奨されません。sourcemapsが有効になっている場合、Angularプロジェクトの元の状態を再現することで、可読性が向上し、ファイル分析に役立ちます。しかし、無効になっている場合、レビュアーはアンチセキュリティパターンを検索することで、コンパイルされたJavaScriptファイルを手動で分析することができます。 さらに、AngularプロジェクトのコンパイルされたJavaScriptファイルは、ブラウザの開発者ツール → Sources(または Debugger and Sources) → \[id].main.js で見つけることができます。有効なオプションに応じて、このファイルの最後に `//# sourceMappingURL=[id].main.js.map` という行が含まれている場合もあれば、**hidden**オプションが**true**に設定されている場合は含まれていないこともあります。それにもかかわらず、**scripts**のためにsourcemapが無効になっている場合、テストはより複雑になり、ファイルを取得することはできません。さらに、プロジェクトビルド中にsourcemapを有効にすることができます。例えば、`ng build --source-map`のように。 ## データバインディング バインディングは、コンポーネントとその対応するビュー間の通信プロセスを指します。これは、Angularフレームワークにデータを送受信するために使用されます。データは、イベント、補間、プロパティ、または双方向バインディングメカニズムを通じて渡すことができます。さらに、データは関連するコンポーネント(親子関係)間や、サービス機能を使用して無関係な2つのコンポーネント間でも共有できます。 バインディングはデータフローによって分類できます: * データソースからビューターゲットへ(_interpolation_、_properties_、_attributes_、_classes_、_styles_を含む);テンプレートで `[]` または `{{}}` を使用して適用できます; * ビューターゲットからデータソースへ(_events_を含む);テンプレートで `()` を使用して適用できます; * 双方向;テンプレートで `[()]` を使用して適用できます。 バインディングは、プロパティ、イベント、属性、およびソースディレクティブの任意のパブリックメンバーに対して呼び出すことができます: | タイプ | ターゲット | 例 | | --------- | -------------------------------------------------------- | -------------------------------------------------------------------- | | プロパティ | 要素プロパティ、コンポーネントプロパティ、ディレクティブプロパティ | \ | | イベント | 要素イベント、コンポーネントイベント、ディレクティブイベント | \ ``` * DOM要素のプロパティを設定するには、`Renderer2.setProperty()`メソッドを使用してXSS攻撃を引き起こすことができます: ```tsx //app.component.ts import {Component, Renderer2, ElementRef, ViewChild, AfterViewInit } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { public constructor ( private renderer2: Renderer2 ){} @ViewChild("img") img!: ElementRef; setProperty(){ this.renderer2.setProperty(this.img.nativeElement, 'innerHTML', ''); } } //app.component.html ``` 私たちの研究中に、`setStyle()`、`createComment()`、および`setValue()`などの他の`Renderer2`メソッドのXSSおよびCSSインジェクションに関する動作も調査しました。しかし、これらのメソッドの機能的制限のため、有効な攻撃ベクターを見つけることはできませんでした。 #### jQuery jQueryは、HTML DOMオブジェクトの操作を助けるためにAngularプロジェクトで使用できる、迅速で小型かつ機能豊富なJavaScriptライブラリです。しかし、このライブラリのメソッドはXSS脆弱性を引き起こすために悪用される可能性があることは知られています。Angularプロジェクトで脆弱なjQueryメソッドがどのように悪用されるかを議論するために、このサブセクションを追加しました。 * `html()`メソッドは、一致した要素のセットの最初の要素のHTMLコンテンツを取得するか、すべての一致した要素のHTMLコンテンツを設定します。しかし、設計上、HTML文字列を受け入れるjQueryのコンストラクタやメソッドは、コードを実行する可能性があります。これは、`"); }); } } //app.component.html

some text here

``` * `jQuery.parseHTML()`メソッドは、文字列をDOMノードのセットに変換するためにネイティブメソッドを使用し、それを文書に挿入することができます。 ```tsx jQuery.parseHTML(data [, context ] [, keepScripts ]) ``` 前述のように、HTML文字列を受け入れるほとんどのjQuery APIは、HTMLに含まれるスクリプトを実行します。`jQuery.parseHTML()`メソッドは、`keepScripts`が明示的に`true`でない限り、解析されたHTML内のスクリプトを実行しません。しかし、ほとんどの環境では、``属性を介してスクリプトを間接的に実行することが可能です。 ```tsx //app.component.ts import { Component, OnInit } from '@angular/core'; import * as $ from 'jquery'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { ngOnInit() { $("button").on("click", function() { var $palias = $("#palias"), str = "", html = $.parseHTML(str), nodeNames = []; $palias.append(html); }); } } //app.component.html

some text

``` ### Open redirects #### DOMインターフェース W3Cのドキュメントによると、`window.location`および`document.location`オブジェクトは、現代のブラウザではエイリアスとして扱われます。したがって、これらは一部のメソッドやプロパティの実装が似ており、これが`javascript://`スキーマ攻撃によるオープンリダイレクトやDOM XSSを引き起こす可能性があります。 * `window.location.href`(および`document.location.href`) 現在のDOMロケーションオブジェクトを取得するための標準的な方法は、`window.location`を使用することです。また、これを使用してブラウザを新しいページにリダイレクトすることもできます。その結果、このオブジェクトを制御することで、オープンリダイレクトの脆弱性を悪用することができます。 ```tsx //app.component.ts ... export class AppComponent { goToUrl(): void { window.location.href = "https://google.com/about" } } //app.component.html ``` 以下のシナリオに対する悪用プロセスは同じです。 * `window.location.assign()`(および`document.location.assign()`) このメソッドは、指定されたURLのドキュメントを読み込み、表示するためにウィンドウを原因します。このメソッドを制御できる場合、オープンリダイレクト攻撃のためのシンクになる可能性があります。 ```tsx //app.component.ts ... export class AppComponent { goToUrl(): void { window.location.assign("https://google.com/about") } } ``` * `window.location.replace()`(および`document.location.replace()`) このメソッドは、現在のリソースを提供されたURLのものに置き換えます。 これは、`assign()`メソッドとは異なり、`window.location.replace()`を使用した後、現在のページはセッション履歴に保存されません。しかし、このメソッドを制御できる場合、オープンリダイレクトの脆弱性を悪用することも可能です。 ```tsx //app.component.ts ... export class AppComponent { goToUrl(): void { window.location.replace("http://google.com/about") } } ``` * `window.open()` `window.open()`メソッドは、URLを受け取り、それが識別するリソースを新しいタブまたはウィンドウに読み込みます。このメソッドを制御することで、XSSまたはオープンリダイレクトの脆弱性を引き起こす機会もあります。 ```tsx //app.component.ts ... export class AppComponent { goToUrl(): void { window.open("https://google.com/about", "_blank") } } ``` #### Angularクラス * Angularのドキュメントによれば、Angularの`Document`はDOMドキュメントと同じであり、Angular内のクライアントサイドの脆弱性を悪用するためにDOMドキュメントの一般的なベクターを使用することが可能です。`Document.location`プロパティおよびメソッドは、以下の例に示すように、成功したオープンリダイレクト攻撃のシンクになる可能性があります: ```tsx //app.component.ts import { Component, Inject } from '@angular/core'; import { DOCUMENT } from '@angular/common'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor(@Inject(DOCUMENT) private document: Document) { } goToUrl(): void { this.document.location.href = 'https://google.com/about'; } } //app.component.html ``` * 研究段階では、オープンリダイレクトの脆弱性についてAngularの`Location`クラスもレビューしましたが、有効なベクターは見つかりませんでした。`Location`は、アプリケーションがブラウザの現在のURLと対話するために使用できるAngularサービスです。このサービスには、与えられたURLを操作するためのいくつかのメソッドがあります - `go()`、`replaceState()`、および`prepareExternalUrl()`。ただし、外部ドメインへのリダイレクトには使用できません。例えば: ```tsx //app.component.ts import { Component, Inject } from '@angular/core'; import {Location, LocationStrategy, PathLocationStrategy} from '@angular/common'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], providers: [Location, {provide: LocationStrategy, useClass: PathLocationStrategy}], }) export class AppComponent { location: Location; constructor(location: Location) { this.location = location; } goToUrl(): void { console.log(this.location.go("http://google.com/about")); } } ``` 結果:`http://localhost:4200/http://google.com/about` * Angularの`Router`クラスは主に同じドメイン内のナビゲーションに使用され、アプリケーションに追加の脆弱性を導入することはありません: ```jsx //app-routing.module.ts const routes: Routes = [ { path: '', redirectTo: 'https://google.com', pathMatch: 'full' }] ``` 結果:`http://localhost:4200/https:` 以下のメソッドもドメインの範囲内でナビゲートします: ```jsx const routes: Routes = [ { path: '', redirectTo: 'ROUTE', pathMatch: 'prefix' } ] this.router.navigate(['PATH']) this.router.navigateByUrl('URL') ``` ## 参考文献 * [Angular](https://angular.io/) * [Angular Security: The Definitive Guide (Part 1)](https://lsgeurope.com/post/angular-security-the-definitive-guide-part-1) * [Angular Security: The Definitive Guide (Part 2)](https://lsgeurope.com/post/angular-security-the-definitive-guide-part-2) * [Angular Security: The Definitive Guide (Part 3)](https://lsgeurope.com/post/angular-security-the-definitive-guide-part-3) * [Angular Security: Checklist](https://lsgeurope.com/post/angular-security-checklist) * [Workspace and project file structure](https://angular.io/guide/file-structure) * [Introduction to components and templates](https://angular.io/guide/architecture-components) * [Source map configuration](https://angular.io/guide/workspace-config#source-map-configuration) * [Binding syntax](https://angular.io/guide/binding-syntax) * [Angular Context: Easy Data-Binding for Nested Component Trees and the Router Outlet](https://medium.com/angular-in-depth/angular-context-easy-data-binding-for-nested-component-trees-and-the-router-outlet-a977efacd48) * [Sanitization and security contexts](https://angular.io/guide/security#sanitization-and-security-contexts) * [GitHub - angular/dom\_security\_schema.ts](https://github.com/angular/angular/blob/main/packages/compiler/src/schema/dom\_security\_schema.ts) * [XSS in Angular and AngularJS](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/XSS%20Injection/XSS%20in%20Angular.md) * [Angular Universal](https://angular.io/guide/universal) * [DOM XSS](https://book.hacktricks.xyz/pentesting-web/xss-cross-site-scripting/dom-xss) * [Angular ElementRef](https://angular.io/api/core/ElementRef) * [Angular Renderer2](https://angular.io/api/core/Renderer2) * [Renderer2 Example: Manipulating DOM in Angular - TekTutorialsHub](https://www.tektutorialshub.com/angular/renderer2-angular/) * [jQuery API Documentation](http://api.jquery.com/) * [How To Use jQuery With Angular (When You Absolutely Have To)](https://blog.bitsrc.io/how-to-use-jquery-with-angular-when-you-absolutely-have-to-42c8b6a37ff9) * [Angular Document](https://angular.io/api/common/DOCUMENT) * [Angular Location](https://angular.io/api/common/Location) * [Angular Router](https://angular.io/api/router/Router)