34
そろそろ押さえておきたい AngularJSのセキュリティ ng-mtg#6 AngularJS勉強会 2014年7月25日 (1.3.0-beta.16対応版)

そろそろ押さえておきたい AngularJSのセキュリティ

Embed Size (px)

DESCRIPTION

ng-mtg#6 AngularJS勉強会の発表資料です。

Citation preview

Page 1: そろそろ押さえておきたい AngularJSのセキュリティ

そろそろ押さえておきたい

AngularJSのセキュリティ

ng-mtg#6 AngularJS勉強会

2014年7月25日

(1.3.0-beta.16対応版)

Page 2: そろそろ押さえておきたい AngularJSのセキュリティ

西村 宗晃 a.k.a. nishimunea

html5j Webプラットフォーム部 部員

HTML5 Experts.jp コントリビューター

セキュリティキャンプ全国大会 2014 講師

FxOS コードリーディング 部員

1

Page 3: そろそろ押さえておきたい AngularJSのセキュリティ

2

Page 4: そろそろ押さえておきたい AngularJSのセキュリティ

3

Page 5: そろそろ押さえておきたい AngularJSのセキュリティ

4

ノウハウを共有して、様々なプラットフォームで

動かすための知識を培おうではないか!

WebはPCの世界を抜け出し

様々なプラットフォームで動き始めています

by めこ部長

Page 6: そろそろ押さえておきたい AngularJSのセキュリティ

本日お話する内容

5

AngularJSで対策できる脆弱性とその実装方法

• DOM Based XSS

• Cross-Site Request Forgery (CSRF)

AngularJSでは対策できない脆弱性 (スコープ外)

• サーバ側での対策が必要となる脆弱性

• ブラウザやプロトコル由来の脆弱性

※CSRFはサーバ側での対策を要しますが今回の発表ではスコープ外とします

Page 7: そろそろ押さえておきたい AngularJSのセキュリティ

DOM Based XSS

Page 8: そろそろ押さえておきたい AngularJSのセキュリティ

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

Page 9: そろそろ押さえておきたい AngularJSのセキュリティ

参考:angularjs.orgにあった反射型XSS

8 ※撮影のためIEのXSSフィルターを無効化しています。本件はGoogleに報告し、既に修正済みです

そのままレスポンスとして出力される

URLに仕込んだスクリプトが… <script>alert(‘XSS’)</script>

Page 10: そろそろ押さえておきたい AngularJSのセキュリティ

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ツリーへの出力経路

※図中の入力経路、出力経路は一例です

Page 11: そろそろ押さえておきたい AngularJSのセキュリティ

DOM Based XSSの対策

10

• 動的にDOMを生成する際は信頼できるデータのみを使用する

- そのままDOMに挿入しても安全だと開発者が保証できるデータのみを使用する

• それ以外の場合は、データを出力するコンテキストに応じた対策を施す

- HTMLの要素内容としてデータを出力する場合

- createTextNodeでテキストノードとして出力する(またはHTMLエスケープ)

- HTMLマークアップとして出力する際は、スクリプトが実行される 恐れの無い要素と属性のみを許可する

- HTML要素の属性値としてデータを出力する場合

- setAttributeで属性値として出力する(またはHTMLエスケープ)

- イベントハンドラ属性などのスクリプトが実行可能な属性値は動的に生成しない

- URLを出力する際は安全なURLであることを検証する

- http(s)://で始まるURLかホワイトリストに該当するオリジンのみを許可する

Page 12: そろそろ押さえておきたい AngularJSのセキュリティ

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を参照

Page 13: そろそろ押さえておきたい AngularJSのセキュリティ

明示的な信頼に基づくエスケープ処理

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>

値に明示的な信頼を与える

Page 14: そろそろ押さえておきたい AngularJSのセキュリティ

データバインド時にコンテキストを自動判別

• $sceを中心に複数のモジュールが連携してデータのコンテキストを自動判別

13

$compile

• テンプレートを解析してexpression展開箇所のコンテキストを判別。判別したコンテキストに応じたエスケープ処理を$interpolateに指示

• 各ディレクティブのtemplateURLを$sceでエスケープ

• ディレクティブの属性値に含まれるURLを$$sanitizeUriでエスケープ

• イベントハンドラ属性のexpression展開を検出&回避

$parse • expressionを解析して危険なオブ

ジェクトの参照を検出&回避

$$sanitizeUri

• URLのエスケープ機能を提供

$interpolate

• コンテキストに応じたexpressionの解析機能を提供

• expressionで展開されるデータを$sceでエスケープ

$sce

• リソースURLとHTMLのエスケープ機能を提供

$sanitize

• リソースURLとHTMLのエスケープ機能を提供

• HTMLの属性値に含まれるURLを$$sanitizeUriでエスケープ

$sceDelegate

• $sceの主要機能を実装

プロバイダ 使用関係

Page 15: そろそろ押さえておきたい AngularJSのセキュリティ

• 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

Page 16: そろそろ押さえておきたい AngularJSのセキュリティ

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が含まれているので削除される

Page 17: そろそろ押さえておきたい AngularJSのセキュリティ

HTMLに対するデータバインド

16

• HTMLのバインド時はexpressionによる文字列の結合が禁止される

- expressionの解析処理が複雑化することによりミスが生じ、 XSSを許容するケースが生じる可能性があるため

- expressionの解析ミスによりJSが実行できてしまう問題が実際にあった <form ng-attr-action="{{'javascript:'}}alert(1)"><button>XSS</button>

※攻撃方法および攻撃可能バージョンの詳細は以下のサイトを参照 http://code.google.com/p/mustache-security/wiki/AngularJS

結合すると javascript:alert(1) となるので本来は無害化されるべき

Page 18: そろそろ押さえておきたい AngularJSのセキュリティ

• AngularJSはCSSのエスケープをサポートしていない

- style要素やstyle属性に挿入したデータはそのままCSSの構文として出力される

- 最近のブラウザではCSSの構文でスクリプトを実行できないためと思われる

CSSに対するデータバインド

17

• IEのQuirksモードではAngularJSの実行が禁止される

- CSS expressionsによるJSの実行が可能であるため

<style>body {color:expression(alert(1));}</style>

QuirksモードではCSSの宣言で JSを実行することができる

※以前はCSS expressions以外にもOperaの-o-linkプロパティで JSを実行する方法がありましたが現在は使えません

http://html5sec.org/#9

Page 19: そろそろ押さえておきたい AngularJSのセキュリティ

• ホワイトリストに合致しない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:」を ホワイトリストに追加する必要があります

Page 20: そろそろ押さえておきたい AngularJSのセキュリティ

• ホワイトリストとブラックリストに基づいてサニタイズされる

- ページを構成するリソースを取得する際に適用される

- img以外のsrc属性、<form action>、SVGのxlink:href、ng-include、 $httpや$resourceの取得先URL、ディレクティブのtemplateUrlプロパティ

- ホワイトリストとブラックリストを用いてサニタイズする

- デフォルトではページと同一オリジン('self')のみを許容

- URLコンテキストより厳しい制限がかかる

リソースURLに対するデータバインド

19

• ホワイトリストとブラックリストは変更可能

- $sceDelegateProviderが両リストを参照/変更する関数を提供している

Page 21: そろそろ押さえておきたい AngularJSのセキュリティ

• ホワイトリストにはワイルドカードを指定できる

- リストには「*」と「**」という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 も許可されてしまう

Page 22: そろそろ押さえておきたい AngularJSのセキュリティ

• 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外の関数が実行できた

Page 23: そろそろ押さえておきたい AngularJSのセキュリティ

• 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をケアする

Page 24: そろそろ押さえておきたい AngularJSのセキュリティ

• コンテキスト毎にデータの取扱い方針を決めておく

カスタムディレクティブを作る際の注意点

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 • 信頼できるデータのみを出力

Page 25: そろそろ押さえておきたい AngularJSのセキュリティ

• HTMLとURLを出力する場合

カスタムディレクティブの実装例

24

.directive(‘myDirective’,[‘$sce’,‘$$sanitizeUri’,

function($sce, $$sanitizeUri) {

return function (scope, element, attrs) {

//HTML Content

element.html($sce.parseAsHtml(attrs.inputExpression)(scope));

//URL Attribute

element.attr('src', $$sanitizeUri(attrs.inputUri));

}

}]);

expressionを安全な HTMLにしてからDOMへ出力

URLをサニタイズして から属性値として出力

Page 26: そろそろ押さえておきたい AngularJSのセキュリティ

• ディレクティブにSCEが適用されていることを確認する

- 例:angular-translateはデフォルトでHTMLをエスケープせずそのまま出力する

- $translateProvider.useSanitizeValueStrategyでエスケープを有効にする必要がある

カスタムディレクティブを使う際の注意点

25

デフォルトでは HTMLエスケープ無効

Page 27: そろそろ押さえておきたい AngularJSのセキュリティ

• 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%低下するそうなので速度が求められる場合は注意

Page 28: そろそろ押さえておきたい AngularJSのセキュリティ

Cross-Site Request Forgery (CSRF)

Page 29: そろそろ押さえておきたい AngularJSのセキュリティ

CSRF発生のメカニズム

28

• 退会APIを例とする。このAPIは退会画面で利用されることを想定している

退会する

本当に退会するの?

退会はこちら

退会処理中です 退会しました

退会APIを実行 CookieでセッションIDが通知される

Page 30: そろそろ押さえておきたい AngularJSのセキュリティ

退会する

本当に退会するの?

退会はこちら

CSRF発生のメカニズム

29

• 退会APIを例とする。このAPIは退会画面で利用されることを想定している

• 罠サイトがAPIを実行した場合、ユーザを意図せず退会させることができる

秘密の画像

♡罠サイト♡ 退会APIを実行

CookieでセッションIDが通知される

退会処理中です 退会しました

被害者

Page 31: そろそろ押さえておきたい AngularJSのセキュリティ

AngularJSのCSRF対策

30

• 正規のページからのリクエストであることを示すトークンをXHRに付加する

退会する

本当に退会するの?

退会はこちら

退会処理中です 退会しました

サーバはXSRF-TOKENという トークン(乱数値)をCookieにセット

AngularJSはトークンをX-XSRF-TOKENという リクエストヘッダに付けて退会API(XHR)を実行

サーバは受信したトークンが 正しい場合のみ退会処理を許可

Page 32: そろそろ押さえておきたい AngularJSのセキュリティ

AngularJSのCSRF対策

31

• 正規のページからのリクエストであることを示すトークンをXHRに付加する

• 有効なトークンが付いていなければ、サーバ側でリクエストを拒否する

不正なリクエスト を検出しました

乱数値が不正なので 退会リクエストを拒否

退会する

本当に退会するの?

退会はこちら

秘密の画像

♡罠サイト♡ 罠サイトはリクエストに正しい 乱数値を付けることができない

被害者

Page 33: そろそろ押さえておきたい AngularJSのセキュリティ

AngularJSでCSRF対策する際の注意点

32

• クロスオリジンのXHRにはX-XSRF-TOKENヘッダが付かない

- $httpや$resourceのリクエストを送信する際に自分でヘッダを付ける

- CORSに基づいてヘッダを受信するための実装がサーバ側にも必要

$scope.method = 'POST';

$scope.url = 'https://api.example.com/resign.php';

$scope.headers = {'X-XSRF-TOKEN': $scope.token };

$http({method: $scope.method, url: $scope.url,

headers:$scope.headers});

リクエストヘッダに有効な トークン値をセットする

※恐らくカスタムヘッダ付きのCORSリクエストはプリフライトを発行してしまうためだと思われます https://github.com/angular/angular.js/issues/1004

Page 34: そろそろ押さえておきたい AngularJSのセキュリティ

謝辞

本資料は竹迫良範様(サイボウズ・ラボ)、佐藤鉄平様(サイボウズ)、

国分裕様(MBSD)に技術レビューをして頂きました。

心より感謝いたします。

お問い合わせはこちら

33