Weitere ähnliche Inhalte
Ähnlich wie そろそろ押さえておきたい AngularJSのセキュリティ (20)
Mehr von Muneaki Nishimura (13)
Kürzlich hochgeladen (11)
そろそろ押さえておきたい AngularJSのセキュリティ
- 2. 西村 宗晃 a.k.a. nishimunea
html5j Webプラットフォーム部 部員
HTML5 Experts.jp コントリビューター
セキュリティキャンプ全国大会 2014 講師
FxOS コードリーディング 部員
1
- 8. XSSの種類
• サーバ側で発生するXSS
- 反射型XSS
- HTTPのリクエストに含まれるスクリプトが、
レスポンスのHTMLにそのまま埋め込まれることで発生
- 持続型XSS
- HTTPのリクエストに含まれるスクリプトが一旦サーバに保存され、
そのデータを元にHTMLを出力する際にスクリプトが埋め込まれることで発生
• クライアント側で発生するXSS
- DOM based XSS
- 外部から取得したデータを元にJSでHTMLを動的生成する際に、
データに含まれるスクリプトがDOMツリーへ出力されることで発生
7
※DOM based XSSも反射型と持続型に分類すべきという意見もあります
https://www.owasp.org/images/f/f4/ASDC12-Unraveling_some_of_the_Mysteries_around_DOMbased_XSS.pdf
- 10. DOM Based XSS発生のメカニズム
9
Web Messaging
$location
Referrer
Web Socket
$cookies
$http, $resource
ngModel
Web Storage
ngBindHtml
ngInclude
element.innerHTML
document.write
jQLite.html
jQLite.append
window.eval
new Function
XSS
スクリプトを
含むデータ
データバインドで
DOMを生成
攻撃者 被害者
データの入力経路 DOMツリーへの出力経路
※図中の入力経路、出力経路は一例です
- 11. DOM Based XSSの対策
10
• 動的にDOMを生成する際は信頼できるデータのみを使用する
- そのままDOMに挿入しても安全だと開発者が保証できるデータのみを使用する
• それ以外の場合は、データを出力するコンテキストに応じた対策を施す
- HTMLの要素内容としてデータを出力する場合
- createTextNodeでテキストノードとして出力する(またはHTMLエスケープ)
- HTMLマークアップとして出力する際は、スクリプトが実行される
恐れの無い要素と属性のみを許可する
- HTML要素の属性値としてデータを出力する場合
- setAttributeで属性値として出力する(またはHTMLエスケープ)
- イベントハンドラ属性などのスクリプトが実行可能な属性値は動的に生成しない
- URLを出力する際は安全なURLであることを検証する
- http(s)://で始まるURLかホワイトリストに該当するオリジンのみを許可する
- 12. Strict Contextual Escaping (SCE)
11
• コンテキストに応じたエスケープ機能を提供するAngularJSの機能
• AngularJS v1.2以降ではデフォルトで有効
利用可能なコンテキスト コンテキストの適用例
HTML <div ng-bind-html=" HTML "></div>
CSS <div style=" CSS ">
URL <a href=" URL ">Link</a>
リソースURL <script src=" リソースURL ">
JS <div onclick=" JS ">
※ここで言うエスケープとは指定されたコンテキストでデータが適切に
表示されるように文字列を変換する処理の総称を意味します。
詳細は https://docs.angularjs.org/api/ng/service/$sceを参照
- 13. 明示的な信頼に基づくエスケープ処理
12
• 安全が保証できる値に明示的な信頼を与えることができる
- 信頼されていない値は自動的にエスケープして出力される
- 逆に言えば、安全が保証できない値に信頼を与えることは脆弱性につながる
var input = '<a href="javascript:alert(1)">Link</a>';
console.log($sce.getTrusted($sce.HTML, input));
var trustedData = $sce.trustAs($sce.HTML, input);
console.log($sce.getTrusted($sce.HTML, trusted));
エスケープして出力される
<a>Link</a>
信頼を与えた値はそのまま出力される
<a href="javascript:alert(1)">Click</a>
値に明示的な信頼を与える
- 15. • AngularJSではデータをHTMLとして出力する手段が制限されている
- HTMLを出力できるのはng-bind-htmlと<iframe srcdoc>のみ
- その他のデータバインドはデータをテキスト値として出力する
- 例:ng-bind, <div>{{expression}}</div>
HTMLに対するデータバインド
14
• スクリプトの実行可能なHTML要素や属性は除去される(サニタイズ)
- 内部処理に$sanitizeを使うのでngSanitizeモジュールをDIする必要がある
- スクリプトが実行される危険性のあるHTML要素や属性は削除される
- 例:<script>alert(‘XSS’);</script><b>Hello</b> World
危険な要素は除去される
var app = angular.module('myApp', ['ngSanitize'] );
※ngSanitizeモジュールをDIしないとunsafeエラーが発生します
https://docs.angularjs.org/#!/error/$sce/unsafe
- 16. HTMLに対するデータバインド
15
• サニタイズにはngSanitizeのホワイトリストが使用される
- ホワイトリストに無いHTML要素や属性は削除される
- 安全な要素でもホワイトリストに含まれていないものがある
- <video>などはリストに無いので削除対象となる
- 属性値に不正なURLが含まれる場合は属性ごと削除される
- 全てのURLが検査対象ではない
- background, cite, href, longdesc, src, usemap属性のみが検査対象
- つまり<html manifest>, <video poster>などのURLは検査対象とならない
- 例:<a href="javascript:alert('XSS');" >Link</a>
※ホワイトリストの詳細は$sanitizeのスクリプトコードを参照
https://github.com/angular/angular.js/blob/master/src/ngSanitize/sanitize.js
href属性はホワイトリストで許可されているが
不正なURLが含まれているので削除される
- 19. • ホワイトリストに合致しないURLは無効化される(サニタイズ)
- aHrefSanitizationWhiteList
- <a href>に指定可能なURLのホワイトリスト
- http:, https:, ftp:, mailto:, tel:, file:で開始するURLのみを許可
- imgSrcSanitizationWhiteList
- <img src>に指定可能なURLのホワイトリスト
- http:, https:, file:, blob:, data:image/ で開始するURLのみを許可
- 上記のホワイトリストに合致しないURLは頭にunsafe:を付けて無効化される
- 例:javascript:alert(1) unsafe:javascript:alert(1)
URLに対するデータバインド
18
• ホワイトリストは変更可能
- $compileProviderがホワイトリストを参照/変更する関数を提供している
※Firefox OSアプリで使う場合は「app:」を、Chromeアプリで使う場合は「chrome-extension:」を
ホワイトリストに追加する必要があります
- 20. • ホワイトリストとブラックリストに基づいてサニタイズされる
- ページを構成するリソースを取得する際に適用される
- img以外のsrc属性、<form action>、SVGのxlink:href、ng-include、
$httpや$resourceの取得先URL、ディレクティブのtemplateUrlプロパティ
- ホワイトリストとブラックリストを用いてサニタイズする
- デフォルトではページと同一オリジン('self')のみを許容
- URLコンテキストより厳しい制限がかかる
リソースURLに対するデータバインド
19
• ホワイトリストとブラックリストは変更可能
- $sceDelegateProviderが両リストを参照/変更する関数を提供している
- 21. • ホワイトリストにはワイルドカードを指定できる
- リストには「*」と「**」という2種類のワイルドカードが指定できる
- ** は全ての文字にマッチする
- ** はURLの末尾のみで使用する
- 安全な例 http://example.com/templates/**
- 危険な例 http://**.example.com/
リソースURLに対するデータバインド
20
※両リストに指定可能なパターンの詳細は以下のURLを参照
https://docs.angularjs.org/api/ng/service/$sce
http://evil.com/q=.example.com も許可されてしまう
- 22. • JSの動的生成は禁止される
- <script>の要素内容や、イベントハンドラ属性に対するexpressionの展開は禁止される
- onで始まる属性、もしくはformaction属性へのデータバインドが禁止される
- 代わりにng-clickなどのイベントハンドラディレクティブが使用できる
- scope外の関数にはアクセスできないのでXSSの被害を軽減できる
JSに対するデータバインド
21
• 任意のコードを実行可能なオブジェクトはexpressionで参照できない
- constructor, window, element, Objectなどはexpressionから参照できない
- Functionコンストラクタを用いて任意のJSが実行する方法が以前指摘されたため
{{constructor.constructor('alert(1)')()}}
※最新版では__proto__の参照や、call, apply, bind関数も実行が禁止されています
以前はFunctionコンストラクタを
参照することでscope外の関数が実行できた
- 23. • XSS対策を自分で行う必要がある
- SCEによる保護は期待できない
- SCEはビルトインのディレクティブに最適化されている
- カスタムディレクティブが出力するHTML要素や属性には
コンテキストの自動判別も適用されない
- 例:<a href>のコードを流用して<video poster>ディレクティブを作成する場合
カスタムディレクティブを作る際の注意点
22
var htmlAnchorDirective = valueFn({
compile: function(element, attr) {
if (!attr.href && !attr.name) {
attr.$set('href', '');
}
$setはposter属性をURLとして判別
しないので自分でURLをケアする
- 24. • コンテキスト毎にデータの取扱い方針を決めておく
カスタムディレクティブを作る際の注意点
23
コンテキスト データの取扱い方針(例)
HTML
• 原則として要素内容はjQLite.text()で出力
• HTMLとして出力する際は$sce.getTrustedHtml()でサニタイズ
• 属性値を出力する際はjQLite.attr()かjQLite.prop() を使用
• イベントハンドラと<iframe srcdoc>に対するデータバインドを禁止
CSS
• style要素やstyle属性はテンプレートに対するデータバインドで出力
(styleタグを組み立ててHTMLとして出力しない)
URL • $$sanitizeUriで無害化したURLを出力
リソースURL • $sce.getTrustedResourceUri()でサニタイズ
JS • 信頼できるデータのみを出力
- 27. • SCEの無効化
- $sceProvider.enabled(false)でSCEを無効化できる
- 無効化するとHTMLとリソースURLがサニタイズされなくなるので注意
SCEに関するその他のこと
26
• Content Security Policy(CSP)の利用
- CSPとは、XSSをはじめとする一般的な脆弱性のリスクを軽減するブラウザの保護機能
- CSPを適用する場合、AngularJSのHTMLテンプレートに以下の変更が必要
- ng-appの指定箇所にng-cspを併記する
- https://code.angularjs.org/snapshot/angular-csp.css をロードする
- CSPを有効にすると実行速度が30%低下するそうなので速度が求められる場合は注意