SlideShare a Scribd company logo
Webアプリ開発者のための

HTML5 セキュリティ入門

Enterprise x HTML5 Web Application Conference 2014
2014年2月28日@明星大学
西村 宗晃 a.k.a. nishimunea
html5j Webプラットフォーム部 部員
FxOS コードリーディング 部員

Gecko勉強会 主催

1
html5j Webプラットフォーム部
• HTMLやJavaScriptでアプリを開発できるOSが増えてきている
• Webの広まりは急速で新しい技術が続々と誕生している

WebやOS固有の技術をみんなで共有して

色んな端末向けのアプリをサックリ公開できたらいいよね!

第一回目の勉強会を近日開催予定です
2
本日お話する内容
サーバと非同期で通信する際の注意点
データを保存する際の注意点
Content Security Policyの適用方法
3
従来のウェブサイト
• ページ遷移の都度、UIを含む全てのデータをサーバから同期的に受信
• サーバとの通信時間が利用者の体感速度に影響
サーバ

クライアント
ページを要求 (同期型)

ページを生成

ページを要求 (同期型)
ページを生成

4
現在のウェブサイト
• UIなどのデータをクライアントに保存し、ローカルでページを生成
• 必要なデータのみをサーバに非同期で要求し、通信のボトルネックを軽減
サーバ

クライアント
ページを要求 (同期型)
データを保存

必要なデータのみを要求 (非同期型)
ページを生成

5
サーバと非同期で通信する際の注意点

6
XMLHttpRequest Level 2

10+

7

4+

4+

5+
次のようなサイトを例に説明します
• UIなどの静的リソースはWebサーバ(example.jp)から取得
• その他のデータはAPサーバ(api.example.jp)から非同期で取得
Webサーバ

ページを要求

example.jp
8

APサーバ

http://example.jp/
データを要求(非同期)

api.example.jp
従来のAjaxの制限
• 従来のAjax(XMLHttpRequest)は異なるオリジンと通信できなかった
• ページの生成元(http://example.jp)以外にはリクエストを送れなかった
Webサーバ

ページを要求

APサーバ

http://example.jp/
データを要求(非同期)

通信不可

example.jp
9

api.example.jp
※オリジンとはあるページやリクエスト、データの生成元(または供給元)のことで、
スキーム名+ホスト名+ポート番号の組み合わせで表される。
このためJSONPが使われるようになった
• JSONのデータをスクリプトファイルとしてロード
• <script>のsrc属性にはクロスオリジンの制限が適用されない点を利用
Webサーバ

ページを要求

example.jp
10

http://example.jp/

<script src>

APサーバ

データをスクリプトファイルとしてロード

api.example.jp
しかしJSONPは脆弱性を作りこみやすかった
• オリジンの制限が無いのでどのサイトからでもデータを取得できる
• しかもJSONPのリクエストには自動的にCookie(セッション情報)が付く
罠サイト

APサーバ

http://wana.jp/
JSONPをロード
(リクエストにCookieが付く)

ワナ

罠サイトを開く

認証済みの利用者のデータが
盗まれてしまう
wana.jp
11

api.example.jp
※JSONPの注意点は他にもあります。詳しくは以下のページをご覧ください
http://a.atmarkit.co.jp/ait/articles/0908/10/news087.html
そこでCORSという仕組みが作られた
• CORS:安全にクロスオリジン通信をするための仕組み
• AjaxもXMLHttpRequest Level 2(XHR2)としてCORSに対応
Webサーバ

ページを要求

Origin: http://example.jp
APサーバ
http://example.jp/
(このリクエストはexample.jpのページから発行されました)
データをリクエスト

Access-Control-Allow-Origin: http://example.jp
example.jp
12

(example.jpからのリクエストのみを許可します)
api.example.jp
※CORSはCross Origin Resource Sharingの略
CORSによってCookieの送信も制限される
• クライアントとサーバの両方で明示的にCookieの送信許可を行う
• 送信を許可しないとXHR2のリクエストにCookieは付かない
Webサーバ

ページを要求

XMLHttpRequest.withCredential=trueAPサーバ

http://example.jp/
(このリクエストにCookieを付けることを許可します)
データをリクエスト

Access-Control-Allow-Credentials: true
example.jp
13

(Cookie付きのリクエストを許可します)
api.example.jp
※CORSには上記以外にも様々な仕様があります。詳しくは以下のページをご覧ください
https://developer.mozilla.org/ja/docs/HTTP_access_control
不正なサイトからのリクエストを拒否するには
Originヘッダでは不正なサイトからのリクエストを判別できない
• 同一オリジンのXHRにはOriginヘッダが付かない
• FORMからの送信やURL直接指定によるリクエストとの区別もできない

XHRであることを示すカスタムヘッダをリクエストに付加する
(カスタムヘッダを用いて正規のXHRであることを判別する)
14
カスタムヘッダには推測できない値を指定する
• XHR以外のリクエストにカスタムヘッダを付ける方法がある
- 参考:mala’s Gists/https://gist.github.com/mala/8857629

• 攻撃者が推測できない値をカスタムヘッダに指定して対処
- 例:セッション固有トークンを使用する
X-CSRF-Token: C90rqt588bq5uivdm1qs…

15

攻撃者が推測できない値を設定する
セッション固有トークン
• セッションID毎に異なるトークン(乱数)をXHRのリクエストヘッダに付加
• 攻撃者はトークンを推測できないので正規のリクエストを真似できない
サーバ
APサーバ

Web Storageに
トークンを保存

事前にトークンを送信
カスタムヘッダにトークンを
付けてXHRを送信

X-CSRF-Token: C90rqt58…
Cookie: SESSION=M5k492c3…
16

SESSION

トークン

M5k492c3…

C90rqt58…

Ju1gkhv7…

Sthf2o5s…

SESSIONとトークンがマッチ
する場合のみXHRを受け入れる

※Web Storageにトークンを保存する際はこの後で紹介する注意点をあわせてご一読ください
XHR2の使用例
XHR2を送信するJavaScript
XHRのリクエストを判別する
ためのカスタムヘッダを付ける
xhr.open("GET", "http://api.example.jp/xhr/");
var xhr = new XMLHttpRequest();

xhr2.setRequestHeader("X-CSRF-Token", "C90rqt58…");
xhr2.withCredentials = "true"; // only when using Cookie
xhr.send();

Cookieを送信することを指定

HTTPリクエストヘッダ (XHR2送信時)

GET http://api.example.jp/xhr HTTP/1.1
X-CSRF-Token: C90rqt58…
Origin: http://example.jp

Cookie: SESSION=M5k492c3…
17

リクエストにOriginヘッダが付加される
XHR2の使用例
HTTPレスポンスヘッダ (api.example.jpが返却)
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.jp
Access-Control-Allow-Credentials: true

リクエストを許可する
オリジンを指定する

リクエストにCookieを付けることを許可する

18
XHR2を受信する際の注意点(サーバ側)
カスタムヘッダの値が有効か?
NO

YES

拒否 (XHR以外)
Originヘッダが付いているか?

NO
YES

許可(同一オリジン)

アクセスを許可しているOriginか?
YES
19

許可(正規オリジン)

NO
拒否(不正オリジン)

• カスタムヘッダの値を確認する(必須)
- 値が有効でない場合はXHR以外のリクエストと
見なしてリクエストを拒否
XHR2を受信する際の注意点(サーバ側)
カスタムヘッダの値が有効か?
NO

YES

• カスタムヘッダの値を確認する(必須)
- 値が有効でない場合はXHR以外のリクエストと
見なしてリクエストを拒否

拒否 (XHR以外)

• Originヘッダの有無を確認する(推奨)
Originヘッダが付いているか?

NO
YES

許可(同一オリジン)

アクセスを許可しているOriginか?
YES
20

許可(正規オリジン)

NO
拒否(不正オリジン)

- 無い場合は同一オリジンからのXHRと見なして
リクエストを許可
XHR2を受信する際の注意点(サーバ側)
カスタムヘッダの値が有効か?
NO

YES

• カスタムヘッダの値を確認する(必須)
- 値が有効でない場合はXHR以外のリクエストと
見なしてリクエストを拒否

拒否 (XHR以外)

• Originヘッダの有無を確認する(推奨)
Originヘッダが付いているか?

NO
YES

許可(同一オリジン)

アクセスを許可しているOriginか?
YES
21

許可(正規オリジン)

NO
拒否(不正オリジン)

- 無い場合は同一オリジンからのXHRと見なして
リクエストを許可

• Originヘッダの値を確認する(推奨)
- 許可しないオリジンであればリクエストを拒否
- Access-Control-Allow-Originヘッダには
リクエスト元のオリジンのみを指定する
→ 許可するオリジンを列挙しない
The WebSocket API

10+

22

16+

11+

6+
WebSocket
• XHR2より低通信コスト&サーバPush可能であるため注目を集めている
• ws:とwss:というスキームを使用(wss:はhttps:に相当)
Webサーバ

ページを要求

example.jp
23

APサーバ

http://example.jp/
ws://api.example.jpに接続

api.example.jp
WebSocketの使用例
http://example.jp/index.html (WebSocketを接続)

var ws = new WebSocket("ws://api.example.jp");

WebSocketの接続を確立

ws.send("msg");
HTTPリクエストヘッダ (WebSocket接続時)
GET http://api.example.jp/ HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: api.example.jp
リクエストにはOriginヘッダが付く
Sec-WebSocket-Key: tJiswgrSqUJu7h5X1W1PKg==
Origin: http://example.jp

24
WebSocketにはオリジンの制限が無い
• どのサイトからでもWebSocketの接続ができる
• しかも接続時には自動的にCookie(セッション情報)が付く
罠サイト

APサーバ

http://wana.jp/
ws://api.example.jpに接続
(リクエストにCookieが付く)

ワナ

罠サイトを開く

認証済みのWebSocketを使って
罠サイトとサーバが自由に通信できる
wana.jp
25

api.example.jp
WebSocket使用時の注意点
• サーバは接続時にOriginヘッダを検証する
- 意図しないオリジンからの接続を拒否するため
- Originヘッダの検証時はホスト名を完全一致で比較する

• クライアントから送られたデータは必ずサーバ側で検証する
- ブラウザ以外のクライアントによるOriginの偽装や正規オリジンのXSS脆弱性により
サーバに不正なデータが送られる可能性があるため

• wss:の使用を検討する
- ws:の通信は暗号化されないので情報が第三者に漏えいする可能性がある

26
サーバでOriginヘッダを検証する際の注意点
• ホスト名は完全一致で比較する
- 脆弱な例: Originに期待するホスト名が含まれていることを確認
if(strpos($_SERVER['HTTP_ORIGIN'], 'example.jp')!== false){
do_process();
example.jp.waru.jpからのリクエストを許可してしまう
- 安全な例: Originヘッダの値と期待するホスト名を完全一致で比較

$url = parse_url($_SERVER['HTTP_ORIGIN']);
if ($url['host'] === 'example.jp'){
do_process();

27
JavaScript Object Notation (JSON)

8+

28

4+

3.5+

4+
JSONに起因する脆弱性
• JSONがブラウザにHTMLとして解釈されることによる脆弱性
- HTMLタグをデータに含んだJSONがHTMLとして解釈される
{"key":"<script>alert(1)</script>"}

スクリプトが実行される可能性がある

• JSONがIEにvbscriptとして実行されることによる脆弱性
- 実行時に呼ばれるエラーハンドラを通じて外部にデータを盗み出す方法がある
- 参考:葉っぱ日記/http://d.hatena.ne.jp/hasegawayosuke/20130517/

• JSONに含まれるデータの取り扱いミスによる脆弱性
- evalでJSONをスクリプトとして解釈することによる不正コード実行
- JSONのデータをHTMLに出力することによるXSS
29
JSONを出力する際の注意点(サーバ側)
• Content-Typeレスポンスヘッダを適切に設定する
- MIME種別と文字コードの両方を正しく指定する
Content-Type: application/json; charset=utf-8

- HTMLタグを含んだJSONがHTMLとして開かれることを防ぐため

• ブラウザによるMIME種別の自動判別を無効化する
- レスポンスヘッダにX-Content-Type-Options: nosniffを指定する
- IEがContent-Typeを無視してJSONをHTMLやvbscriptとして開くことを防ぐため
http://api.example.jp/json.php/index.html

PATH_INFOに含まれる.htmlを
IEが誤解釈してHTMLとして開く
30
JSONを処理する際の注意点(クライアント側)
• JSONの解析にevalを使用しない
- JSON.parseを使用する
- JSONに含まれる不正なスクリプトを実行してしまう恐れがあるため
{"token":""+alert(1)+""}

31
JSONを処理する際の注意点(クライアント側)
• JSONの値をそのままHTMLとして出力しない
- テキストノードとして出力する
document.getElementById('foo').textContent = json.data;

- HTMLタグのエスケープは不具合を生みやすいので極力使用を避ける
→ クライアントとサーバのどちらでエスケープするかが明確である場合のみ使用すべき
→ 例えば既にエスケープされているデータをクライアントで二重エスケープしてしまう
Puzzle &amp; Dragons

Puzzle &amp;amp; Dragons
画面表示「Puzzle &amp; Dragons」

32
JSONを処理する際の注意点(クライアント側)
• JSONに含まれる値をそのままHTMLとして出力しない
- やむ無くHTMLとして出力する場合はHTMLエンティティエスケープを行う
function escape_html( s ){
return s.replace( /&/g, "&amp;" )

.replace( /</g, "&lt;" )
.replace( />/g, "&gt; " )
.replace( /"/g, "&quot;" )

HTMLの構文として意味を持つ
& < > “ ‘ をエスケープする

.replace( /'/g, "&#39;" );
}
document.writeln( escape_html(json.data) );
※HTML5ではシングルクォート(‘)の文字参照に&apos;が割り当てられているが、IE8が解釈しないので&#39;を使用する

33
JSONを処理する際の注意点(クライアント側)
• 信頼できない値をイベントハンドラ属性に出力しない
- 不正なスクリプトを実行する可能性がある
document.writeln('<a href=# onclick="'+json.func+'">Send</a>');
- 不正なコードかどうかを検証することも難しい

34
JSONを処理する際の注意点(クライアント側)
• URLとして使用する際はhttp:とhttps:スキーム以外を許可しない
- JSONに含まれるURLがhttp://またはhttps://で始まることを確認する
if( json.url.match( /^https?:¥/¥// ) ){
location.href = json.url;

}
参考:「HTML5 を利用したWeb アプリケーションのセキュリティ問題に関する調査報告書」JPCERT 著

- URLの検証コードをブラックリスト方式で実装しない
if( json.url.match( /^javascript:/ ) ){
return;

}
35

Java

script:alert(1); を許可してしまう

※他にもjAvAsCrIpT:など様々な回避方法がある
※JSONに含まれるURLへリダイレクトする際はオープンリダイレクトの脆弱性にも注意してください(詳細は下記)
http://utf-8.jp/public /20131114/owasp.pptx
データを保存する際の注意点

36
Web Storage

8+

37

4+

3.5+

4+
クライアントにデータを保存する技術が発達
•
•
•
•
•

Offline Web Application
File API: Writer
Indexed Database API
Web Storage
Cookie

これまでサーバ側やCookieに保存していたデータを
Web Storageに保存する際に注意すべき点は?
38
Web StorageとCookieの比較
Web Storage

Cookie

オリジン単位
(scheme+host+port)

domainとpath単位
+secure属性

禁止できない

禁止できる
(httponly属性)

サーバへの送信

明示的に送信しない限り
送られない

HTTPリクエストの都度
自動的に送信される

データの有効期限

無期限(LocalStorage)・
ウィンドウが閉じるまで
(SessionStorage)

任意の期限をサーバから指定

アクセス制御の単位

JSからのアクセス

39
アクセス制御の単位について説明します
Web Storage

Cookie

オリジン単位
(scheme+host+port)

domainとpath単位
+secure属性

禁止できない

禁止できる
(httponly属性)

サーバへの送信

明示的に送信しない限り
送られない

HTTPリクエストの都度
自動的に送信される

データの有効期限

無期限(LocalStorage)・
ウィンドウが閉じるまで
(SessionStorage)

任意の期限をサーバから指定

アクセス制御の単位

JSからのアクセス

40
アクセス制御の単位(Web Storage)
• 同一生成元ポリシーに基づくアクセス制御

example.jp

- scheme + host + port の組み合わせが同じ
ホストからのみアクセスが許可される
アクセス可能

http://a.example.jp

- IEはscheme+hostでアクセス制御する(portを見ない)
- IE8は同じhost内でデータを共有する(schemeも見ない)

• http://a.example.jpのデータに対して
次のホストはアクセスできない

https://a.example.jp

- http://a.example.jp:8080
- https://a.example.jp
- http://example.jp

http://b.example.jp
41

※この他にSessionStorageのデータは他のウィンドウと共有されないという特徴もあります(詳細は下記)
https://developer.mozilla.org/ja/docs/DOM/Storage
アクセス制御の単位(Cookie)
example.jp

• a.example.jpは次のdomainに対する
Cookieを発行できる
- a.example.jp (デフォルト)
- example.jp

http://a.example.jp
https://a.example.jp

http://b.example.jp
42
アクセス制御の単位(Cookie)
• a.example.jpは次のdomainに対する
Cookieを発行できる

example.jp
アクセス可能

http://a.example.jp
https://a.example.jp

http://b.example.jp
43

- a.example.jp (デフォルト)
- example.jp

• Cookieはhttp:とhttps:で共有される
アクセス制御の単位(Cookie)
• a.example.jpは次のdomainに対する
Cookieを発行できる

example.jp

- a.example.jp (デフォルト)
- example.jp

http://a.example.jp
アクセス可能

https://a.example.jp

http://b.example.jp
44

• Cookieはhttp:とhttps:で共有される

• Cookieにsecure属性を指定した場合、
https通信時のみサーバへ送信される
- https://a.example.jp のみで利用できる
アクセス制御の単位(Cookie)
example.jp

アクセス可能

• a.example.jpは次のdomainに対する
Cookieを発行できる
- a.example.jp (デフォルト)
- example.jp

http://a.example.jp
https://a.example.jp

http://b.example.jp

• Cookieはhttp:とhttps:で共有される

• Cookieにsecure属性を指定した場合、
https通信時のみサーバへ送信される
- https://a.example.jp のみで利用できる

• example.jpのCookieはexample.jp
内の全てのサブドメインから利用できる
- b.example.jpからもアクセスできる

45
Web Storageを使用する際の注意点
• 永続性の必要ないデータはSessionStorageに保存する
- データの消し忘れによるリスクを軽減できる

• LocalStorageのデータの消し忘れに注意する
- 例えば利用者がサーバからログアウトした際は忘れずにデータを削除する

• サーバとのセッション情報はCookieに保存する
- CookieはJSからのアクセス禁止やサーバからの強制削除が可能であるため

• XSS脆弱性の対策をしっかりやる
- XSSによって攻撃者に全てのデータを盗まれる可能性があるため
- CSP(後述)の適用によりXSSのリスクを軽減する
46
Cookieを使用する際の注意点
• domainを指定しない
- 同じドメイン内の他のサイトを通じてデータが漏えいする可能性があるため

• secure属性の使用を検討する
- 特にhttpsのみで提供しているサイトはsecure属性の適用を必須とする

• セッション情報を含むCookieにはhttponly属性を付ける
- XSSでセッション情報が盗まれるリスクを軽減できるため

• 有効期限はなるべく短めに設定する
- サービスの利便性とビジネスリスクのバランスを考慮した上で決める
- 利用者がログアウトした際はサーバから強制削除する
47
ついでにデータの保存に関する注意点
• 必要の無いデータは保存しない
- クレジットカードのセキュリティコード
- 利用者のパスワード → ID連携の利用、HASHをかけてサーバに保存

• ビジネスリスクのあるデータはサーバ側で管理し、
クライアントには送信しない
- 利用者のクレジットカード番号 → 下4桁の開示も極力避ける
- 利用者の権限や資格に関する情報(購入済みフラグ、管理者フラグ)
- クライアント側のデータには暗号化などの保護が行われないためデータの漏えいや
改ざんのリスクがあることを意識する

48
Content Security Policyの適用方法

49
Content Security Policy

10+

50

14+

4+

6+
ブラウザのセキュリティモデル
• 最近のブラウザは一般的な攻撃に対する様々な保護機能を備えている
• HTTPレスポンスヘッダを通じてWebサーバからそれらの機能を制御できる
Webサーバ

HTTPリクエスト
HTTPレスポンス

HTTPレスポンスヘッダで
ブラウザの防御機能を制御
51

•
•
•
•
•

XSSフィルタ
フレーム内でのページ表示制限
HTTPSの強制使用(HSTS)
公開鍵ピンニング(HPKP)
Content-Security-Policy
セキュリティ関連のHTTPレスポンスヘッダ
ヘッダ名
X-XSS-Protection

ブラウザの反射型XSS防止機能を有効/無効化する

X-Frame-Options

ブラウザにフレーム内でのページ表示可否を指定する

Strict-Transport-Security

ブラウザにHTTPSの使用を強制する

Public-Key-Pins

指定したSSL証明書以外によるHTTPS通信を遮断する

Content-Security-Policy

一般的な攻撃に対するブラウザの保護機能を有効化する

Content-Disposition

ブラウザに送信したコンテンツの取り扱い方法を指示する

X-Content-Type-Options

ブラウザによるコンテント種別の自動判別を無効化する

X-Download-Options
52

用途

IEのダウンロードダイアログの「開く」や「保存」を無効化
今日はContent-Security-Policyを紹介します
ヘッダ名
X-XSS-Protection

ブラウザの反射型XSS防止機能を有効/無効化する

X-Frame-Options

ブラウザにフレーム内でのページ表示可否を指定する

Strict-Transport-Security

ブラウザにHTTPSの使用を強制する

Public-Key-Pins

指定したSSL証明書以外によるHTTPS通信を遮断する

Content-Security-Policy

一般的な攻撃に対するブラウザの保護機能を有効化する

Content-Disposition

ブラウザに送信したコンテンツの取り扱い方法を指示する

X-Content-Type-Options

ブラウザによるコンテント種別の自動判別を無効化する

X-Download-Options
53

用途

IEのダウンロードダイアログの「開く」や「保存」を無効化
Content-Security-Policy(CSP)とは
• 一般的な攻撃の被害を軽減するために作られた保護機能
- 現行(CSP1.0)はXSSの軽減を目的とする
- 次版(CSP1.1)ではクリックジャッキングなども対象となる予定

• HTTPレスポンスヘッダにセキュリティポリシーを定義
- 通信を許可するオリジンなどをポリシーとしてヘッダに記載する
- 2種類のHTTPレスポンスヘッダが使用できる
- Content-Security-Policy
- ポリシーに違反する処理を無効化
- Content-Security-Policy-Report-Only
- 無効化はせず、サイトの管理者に違反レポートのみを送信

54
CSPヘッダの名前はブラウザごとに異なる
• 将来的にはContent-Security-Policyに統一される見込み
• 現時点では以下の3種類を全て付けておくのが無難
ヘッダ名

IE

Content-Security-Policy
Content-Security-Policy-Report-Only
X-Content-Security-Policy
X-Content-Security-Policy-Report-Only

X-WebKit-CSP
X-WebKit-CSP-Report-Only
55

Chrome Firefox
25+

10+

23+

Safari
7+

4+
14+

6
ポリシーの定義に使用するディレクティブ
ディレクティブ名

default-src

明示的な指定の無い全コンテンツ

script-src

スクリプトファイル

<script>, Worker, SharedWorker, XSLT

object-src

プラグイン

<object>, <embed>, <applet>

style-src

スタイルシート

img-src

画像ファイル

media-src

メディアファイル

<video>, <audio>, <source>, <track>

frame-src

フレームコンテンツ

<iframe>, <frame>

font-src

Webフォント

cssの@font-face

connect-src
56

ポリシーの適用対象

影響を受ける要素の例

サーバとの非同期通信

WebSocket, EventSource, XMLHttpRequest.open

<link rel="stylesheet">, document.styleSheets,
cssの@import
<img>, <link rel="shortcut icon">, cssのurl()や
image()
ポリシーの書き方
• まずはdefault-srcに'none'を指定
- 初期値は '*' で、全ての通信元を許可するため
default-src 'none';

• コンテンツの種別ごとにアクセスを許可するオリジンを記載
- 例:自身のサイトのJavaScriptの使用+jQueryをCDNで利用する場合
default-src 'none'; script-src 'self' http://code.jquery.com;

57
CSPによるインラインスクリプトの実行制限
• default-srcやscript-srcを指定すると、HTMLファイル内の
インラインスクリプトの実行が禁止される
- scriptタグ内のスクリプト記述
<div><script>document.writeln("hello");</script></div>

実行できない

- javascript: URLによるスクリプト記述

<a href="javascript:show_popup();">Click Here</a>

実行できない

- HTMLタグのイベントハンドラ属性
<input id="email" onclick="show_popup('hello')">

58

実行できない
CSPによるインラインスクリプトの実行制限
• HTMLとJSファイルを分離するための改修が必要となる
- scriptタグ内のスクリプト記述
<div><script src="hello.js"></script></div>

HTMLとJSのファイルを分離

- HTMLタグのイベントハンドラ属性
<input id="email" data-arg="hello">

JSに渡す引数をdata属性で指定

var email = document.getElementById('email');
email.onclick = function(){
show_popup(email.getAttribute("data-arg"));
}
59

JSファイル内でイベントハンドラを設定
CSPによるインラインスクリプトの実行制限
• script-srcにunsafe-inlineを指定することにより、インライン
スクリプトの実行制限を外すことができる
- HTMLとJSの分離には時間を要するため、unsafe-inlineを指定した状態でCSPをデプロイし、
それから分離作業を進める

60
CSPによるevalの実行制限
• default-srcやscript-srcを指定すると、evalに代表される
文字列→JSコードへの変換を伴う関数の実行が禁止される
- eval関数

eval("2 + 2");

実行できない

- Functionコンストラクタ

var multiply = new Function("x", "y", "return x * y");

実行できない

- setIntervalやsetTimeout (第一引数に文字列を指定した場合)
setInterval("alert('hello')", 1000);

61

実行できない
CSPによるevalの実行制限
• script-srcにunsafe-evalを指定することにより、文字列から
コードを生成する関数の使用制限を外すことができる
- unsafe-inlineと同様に、unsafe-evalを指定してCSPをデプロイした後、
計画を立てて順にevalの廃止を進める
- 主要なJavaScript MVCフレームワークの多くもunsafe-evalの指定が必須

62
ポリシー違反のレポートについて
• CSPヘッダにreport-uriを指定
- report-uriに違反レポートを受信するURL(同一オリジン)を指定する
report-uri http://example.jp/report.php;
- csp-reportというJSON形式のレポートがPOSTで送信される

• 違反レポートの誤検知をフィルターする必要がある
- 表示中のページにJavaScriptやCSSを挿入するブラウザ拡張があるため
→ 例えばFirefoxアドオンのFirebugを使用すると大量の違反レポートが送られる
- レポートの傾向から誤検知を取り除く仕組みを構築すべき

63
CSPを使用する際の注意点
• Content-Security-Policy-Report-Onlyで運用開始する
- 稼働しているシステムに影響を与えないようにする

• default-src 'none' を指定するところからスタート
- それからコンテンツ種別ごとに許可するオリジンを指定する
- 初めのうちはunsafe-inlineとunsafe-evalを許容しても良い

• 違反レポートの誤検知をフィルタリングする仕組みを用意する
- 誤検知を取り除き、攻撃の兆候を見逃さないようにする

64
まとめ

65
本日ご紹介した注意点
XMLHttpRequest Level 2
• カスタムヘッダにユニークな値が含まれていることを確認する

• Originヘッダの有無、ある場合は許可するオリジンであることを確認する
• Access-Control-Allow-Originに許可する全てのオリジンを列挙しない

WebSocket
• 接続時にOriginヘッダの値を完全一致で確認する
• クライアントから送られたデータは必ずサーバ側で検証する

• wss:の使用を検討する

66
本日ご紹介した注意点
JSON
• Content-Typeを正しく指定する

• X-Content-Type-Options: nosniffを指定する
• evalではなくJSON.Parseで解析する
• HTMLに値を出力する際はテキストノードとして出力する

• http:とhttps:以外のURLを許可しない

67
本日ご紹介した注意点
Web Storage
• 永続性が必要無いならSessionStorageを使用する

• LocalStorageのデータの消し忘れに注意する
• セッション情報はWeb StorageではなくCookieに保存する
• XSS脆弱性の対策をしっかりやる

Cookie
• domainを指定しない
• httponly属性とsecure属性を付ける
• 有効期限はなるべく短くする
68
本日ご紹介した注意点
データの保存全般
• 必要の無いデータを保存しない

• ビジネスリスクのあるデータはサーバに保存し、クライアントに出力しない

Content-Security-Policy (CSP)
• Content-Security-Policy-Report-Onlyで運用開始する
• default-src 'none' を指定する
• 違反レポートの誤検知をフィルタリングする仕組みを用意する

69
ご清聴ありがとうございました

謝辞
本資料は株式会社セキュアスカイ・テクノロジーの新井様、坂本様に
技術レビューをして頂きました。心より感謝いたします。
70

More Related Content

Webアプリ開発者のためのHTML5セキュリティ入門