Webアプリケーション開発に潜むリスクのケーススタディ

25.9K Views

March 07, 24

スライド概要

サードパーティークッキーの廃止やクッキーのSameSite属性等によるCSRFやXSSへの影響などを中心にしたセキュリティ講座です。

元はFindyさんのイベントでの登壇資料です
https://findy.connpass.com/event/310778/

profile-image

徳丸本の中の人 OWASP Japanアドバイザリーボード EGセキュアソリューションズ取締役CTO IPA非常勤職員 YouTubeチャンネル: 徳丸浩のウェブセキュリティ講座 https://j.mp/web-sec-study

シェア

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

Webアプリケーション開発に潜むリスクのケーススタディ EGセキュアソリューションズ株式会社 徳丸 浩 徳丸浩のウェブセキュリティ講座

2.

徳丸浩の自己紹介 • 経歴 – 1985年 京セラ株式会社入社 – 1995年 京セラコミュニケーションシステム株式会社(KCCS)に出向・転籍 – 2008年 KCCS退職、HASHコンサルティング株式会社(現社名:EGセキュアソリューションズ株式会社)設立 • 経験したこと – 京セラ入社当時はCAD、計算幾何学、数値シミュレーションなどを担当 – その後、企業向けパッケージソフトの企画・開発・事業化を担当 – 1999年から、携帯電話向けインフラ、プラットフォームの企画・開発を担当 Webアプリケーションのセキュリティ問題に直面、研究、社内展開、寄稿などを開始 – 2004年にKCCS社内ベンチャーとしてWebアプリケーションセキュリティ事業を立ち上げ • 現在 – – – – EGセキュアソリューションズ株式会社取締役CTO https://www.eg-secure.co.jp/ 独立行政法人情報処理推進機構 非常勤研究員 https://www.ipa.go.jp/security/ 著書「体系的に学ぶ 安全なWebアプリケーションの作り方(第2版)」(2018年6月) YouTubeチャンネル「徳丸浩のウェブセキュリティ講座」 https://j.mp/web-sec-study – 技術士(情報工学部門) 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 2

3.

アジェンダ • Cookieを巡る状況 – SameSite属性 – サードパーティクッキー廃止 • CORS – CORS設定不備による情報漏洩 • クロスサイトリクエストフォージェリ(CSRF) – CORS設定不備 – メソッドオーバーライド – 中間者攻撃 • JavaScriptによる攻撃はバイパスできる • クロスサイトスクリプティング(XSS) 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 3

4.

Cookieを巡る状況 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 4

5.

CookieのSameSite属性について • SameSite属性は、異なるサイトから遷移した場合に、クッキーを送信 するかどうかを制御するもの 元々Cookieがセットされていた場合に、Cookieが送信されるか SameSite=None(旧来のデフォルト) example.jpのCookieを trap.example.com trap.example.com (罠サイト) POST,GET 常に送信 SameSite=Lax(Google Chromeの現在のデフォルト) example.jpのCookieを SameSite=Strict POST,GET GETのみ送信 example.com example.jp (攻撃対象サイト) example.jpのCookieを POST,GET 徳丸浩のウェブセキュリティ講座 一切送信しない © 2024 Hiroshi Tokumaru 5

6.

徳丸浩のウェブセキュリティ講座 https://xtech.nikkei.com/atcl/nxt/column/18/02731/012600002/ 6

7.

サードパーティクッキー • 下図は、サイトAでセットされたCookieが、他のサイトのiframe等の 中でも同じCookieとして扱われることを示す • 元々のCookieの仕様が、トラッキングに利用され、プライバシー上の 問題になってきた サイトA Cookie: A=1234 サイトB サイトA Cookie: A=1234 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru サイトC サイトA Cookie: A=1234 7

8.

Google Chromeのサードパーティクッキー • 下図は、サイトAでセットされたCookieが、他のサイトのiframe等で は送信されない(セットもされない)ことを示す • 元々はトラッキング防止のための施策だが、セキュリティを高める効 果もある • クリックジャッキングや、 iframeやXHR/Fetchを使ったXSS、CSRF等 の緩和策として効果がある サイトA Cookie: A=1234 徳丸浩のウェブセキュリティ講座 サイトB サイトC サイトA サイトA クッキーはセットも 送信もされない クッキーはセットも 送信もされない © 2024 Hiroshi Tokumaru 8

9.

FirefoxのTotal Cookie Protection • 下図は、サイトAでセットされたCookieが、他のサイトのiframe等で は別のCookieとして扱われることを示す • 元々はトラッキング防止のための施策だが、セキュリティを高める効 果もある サイトA Cookie: A=1234 サイトB サイトA Cookie: A=2345 サイトC サイトA Cookie: A=3456 • 参考: 2023年4月においてクリックジャッキング未対策のサイトはどの条件で被害を 受けるか https://blog.tokumaru.org/2023/04/clickjacking-condition.html 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 9

10.

CORS(Cross-Origin Resource Sharing) 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 10

11.

この項で説明すること • JavaScript で複数サーバをまたがって 通信する(XMLHttpRequestや Fetch API)場合には CORS(Cross-Origin Resource Sharing) の理解が不 可欠です • しかし、最近は「CORSはフレームワークにまかせておけば大丈夫」 という風潮があるようです • フレームワーク任せのCORS対応では、大きな落とし穴があることを 説明します 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 11

12.
[beta]
CORSがなかった時代は同一オリジンのみ通信できた
Webサーバー
https://www.example.com
HTML
<div>xxx</div>
<script> … </script>

JSON
{ "id": 123 }

同一オリジンとは、
スキーム(https)
ホスト(www.example.com)
ポート(443)
がすべて同一である状態

IE7

e

var req = new XMLHttpRequest();
req.open("GET", "/api");

徳丸浩のウェブセキュリティ講座

© 2024 Hiroshi Tokumaru

12

13.

CORSがなかった頃のセキュリティ保護=同一オリジンポリシー 罠サイト https://evil.example.org Webサーバー https://www.example.com HTML IE7 IE7 e CORSがないと、このリクエストは エラーになっていた = 安全だが不便 var req = new XMLHttpRequest() req.open("GET", "https://www.example.com/api") 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 13

14.
[beta]
CORSによるセキュリティ保護(現在のブラウザ)
罠サイト
https://evil.example.org

Webサーバー 兼 APIサーバー
https://www.example.com

{

HTML

}

"email": "alice@example.jp",
"tel": "03-1290-5678"

Google Chrome
Error: CORSヘッダがない
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://evil.example.org
const req = new XMLHttpRequest()
req.open("GET", "https://www.example.com/api")
req.withCredentials = true

徳丸浩のウェブセキュリティ講座

© 2024 Hiroshi Tokumaru

クッキーは飛びレスポンスも返るが、
上の2行が返されないと、
レスポンスは受け取れない
14

15.

単純リクエスト ≒ 従来CSRF対策が必要だった条件 • メソッドは以下のいずれか – GET – POST – HEAD • 設定できるリクエストヘッダは以下のいずれか(主要なもの) – – – – Accept Accept-Language Content-Language Content-Type (但し、下記の要件を満たすもの) • Content-Typeヘッダは以下のいずれか – application/x-www-form-urlencoded – multipart/form-data – text/plain 詳しくは以下を参照 https://developer.mozilla.org/ja/docs/Web/HTTP/CORS#Simple_requests 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 15

16.
[beta]
単純リクエストでない場合はプリフライトリクエストが飛ぶ
ブラウザ
www.example.com

api.example.com

GET / HTTP/1.1
www.example.com
const req = new XMLHttpRequest()
req.open("PUT",
"https://api.example.com/api")
req.withCredentials = true
req.setRequestHeader('Content-type',
'application/json')
req.send('{"email":"alice@example.co
m"}')

HTTP/1.1 200 OK
Content-Length: 832

OPTIONS /api …

この後PUTしていいですか?

HTTP/1.1 200 OK

いいですよ

PUT /api …

許可いただいたのでPUTしますね

HTTP/1.1 200 OK

徳丸浩のウェブセキュリティ講座

レスポンス受け取ってください

© 2024 Hiroshi Tokumaru

16

17.

Cookieを伴うXHRは厳しい条件が課せられている • CORSのルールは複雑だが、Cookieを使わない場合は、設定が甘くて も実害がないケースが多い • Cookieを伴う場合は間違えると大変! – 【必須】Access-Control-Allow-Credentials: true – 【必須】 Access-Control-Allow-Origin: http://www.example.org – Access-Control-Allow-Origin: * ではレスポンスを受け取れない • Cookieを伴わないXHRは Access-Control-Allow-Origin: * でレスポンス を受け取れるが、Cookieがない=認証がない ので通常大問題ではない 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 17

18.
[beta]
Express.js の脆弱な設定例
const express = require('express')
const cors = require('cors') // 便利なパッケージ
const app = express()
app.use(cors({ origin: true, credentials: true }))
// ...

全部OKの危険な状況

OPTIONS / HTTP/1.1
User-Agent: Mozilla/5.0
Accept: */*
Origin: https://evil.example.org
Host: www.example.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: x-evil
Referer: https://evil.example.org/

HTTP/1.1 204 No Content
X-Powered-By: Express
Access-Control-Allow-Origin:
https://evil.example.org
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods:
GET,HEAD,PUT,PATCH,POST,DELETE
Access-Control-Allow-Headers: x-evil
Content-Length:
0
Access-Control-Allow-Origin:
https://evil.example.org
Date: Wed, 29
Sep 2021 07:55:24 GMT
Access-Control-Allow-Credentials:
true
Connection:
keep-alive
Access-Control-Allow-Methods:
GET,HEAD,PUT,PATCH,POST,DELETE
Keep-Alive:
timeout=5
Access-Control-Allow-Headers:
x-evil

徳丸浩のウェブセキュリティ講座

© 2024 Hiroshi Tokumaru

18

19.

CORS設定不備による情報漏洩 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 19

20.
[beta]
CSRFと似た罠だけど、CSRFと違い情報が漏れる
<body>
<script>
単にクロスオリジンでAPIを呼ぶだけ
async function getTodos() {
const response = await fetch("https://todo.example.jp/spa/api/user", {
credentials: "include",
})
const todos = await response.text()
const div = document.getElementById("display")
サードパーティ
div.textContent = todos
クッキーの許可状
}
況により結果が変
</script>
わる
CORS脆弱性の罠
<input type="button" value="実行" onclick="getTodos()" />

<div id="display"></div>
</body>
徳丸浩のウェブセキュリティ講座

© 2024 Hiroshi Tokumaru

20

21.

クロスサイト・リクエストフォージェリ(CSRF) 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 21

22.
[beta]
脆弱な機能(メールアドレス変更)の正常系
ブラウザ
example.jp

GET /userform HTTP/1.1
example.jp

const req = new XMLHttpRequest()
req.open("POST", "/user")
req.send(
'{"mail": "sato@example.jp"}');

HTTP/1.1 200 OK
Set-Cookie: SESSID=xxxx
Content-Length: 832

POST /user HTTP/1.1
Cookie: SESSID=xxxx
Referer: https://example.jp/userform
Origin: https://example.jp

更新

{"mail":"sato@example.jp"}
徳丸浩のウェブセキュリティ講座

© 2024 Hiroshi Tokumaru

22

23.
[beta]
罠サイトからCSRF攻撃する模様
ブラウザ
evil.example.com

example.jp

GET /trap HTTP/1.1
evil.example.com

const req = new XMLHttpRequest()
req.open("POST",
"https://example.jp/user")
req.withCredentials = true
req.send('{"mail":
"cracked@example.com"}');

HTTP/1.1 200 OK
Content-Length: 832

POST /user HTTP/1.1
Cookie: SESSID=xxxx
Referer: https://evil.example.com/trap
Origin: https://evil.example.com

{"mail":"cracked@example.com"}
徳丸浩のウェブセキュリティ講座

© 2024 Hiroshi Tokumaru

23

24.
[beta]
PUTメソッドでJSONならプリフライトリクエストが必要では?
PUT /spa/api/profile HTTP/1.1
Host: todo.example.jp
Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ…
Content-Type: application/json
Content-Length: 48
User-Agent: Mozilla/5.0
Accept: */*
Origin: https://todo.example.jp
Referer: https://todo.example.jp/spa/editprofile
{"userid":"wasbook","email":"tanaka@example.jp"}
徳丸浩のウェブセキュリティ講座

© 2024 Hiroshi Tokumaru

24

25.

CORS設定の脆弱性があれば大丈夫、 攻撃できます! 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 25

26.
[beta]
application/jsonなエンドポイントへのCSRF攻撃
クロスオリジンのXHR/Fetch API
• クロスオリジンでXHR…CORSの脆弱性が前提
<body>
<script>
function submitRequest() {
const xhr = new XMLHttpRequest()
xhr.open("PUT", "https://todo.example.jp/spa/api/profile", true)
xhr.setRequestHeader("Content-Type", "application/json")
xhr.withCredentials = true
var body = '{"userid":"wasbook","email":"crack@example.com"}'
xhr.send(body)
}
submitRequest();
</script>
<input type="button" value="Submit request" onclick="submitRequest();" />
</body>
徳丸浩のウェブセキュリティ講座

© 2024 Hiroshi Tokumaru

26

27.

CORS設定が正しければOK? 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 27

28.
[beta]
メソッドオーバーライドによる攻撃
• メソッドオーバーライドによりPUTメソッドを使う
<form action="https://todo.example.jp/spa/api/profile?_method=PUT" method="post">
<input type="hidden" name="userid" value="cracked">
メソッドオーバーライドにより、
<input type="hidden" name="email" value="cracked@example.com">
PUTメソッドと解釈される
<input type="submit">
</form>
POST /spa/api/profile?_method=PUT HTTP/1.1
Host: todo.example.jp
Cookie: SESSID=aa19f9930b68c2ae02c8792a74ade572;
Content-Length: 42
Content-Type: application/x-www-form-urlencoded
Origin: https://trap.example.org
Referer: https://trap.example.org/csrfpoc.html

Demo

userid=cracked&email=cracked%40example.com

徳丸浩のウェブセキュリティ講座

© 2024 Hiroshi Tokumaru

28

29.

SameSite=LaxやStrictならOK? 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 29

30.

偽アクセスポイントによる中間者攻撃 偽AP MITM PC(Burp Suite) 被害者のPC 正規サイト AP インターネット ルーター ※ 偽APの立て方に興味がある人は以下の記事を読んでください(悪用厳禁) HTTPSを使ってもCookieの改変は防げないことを実験で試してみた https://blog.tokumaru.org/2013/09/cookie-manipulation-is-possible-even-on-ssl.html 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 30

31.
[beta]
中間者攻撃により同一サイト(HTTP)からCSRF攻撃する模様
ブラウザ

中間者
example.jp(HTTP)

example.jp

GET /trap HTTP/1.1
evil.example.com

const req = new XMLHttpRequest()
req.open("POST",
"https://example.jp/user")
req.withCredentials = true
req.send('{"mail":
"cracked@example.com"}');

HTTP/1.1 200 OK
Content-Length: 832

同一サイトからの攻撃
なのでSameSite=Laxで
もCookieが飛ぶ

POST /user HTTP/1.1
Cookie: SESSID=xxxx
Referer: https://evil.example.com/trap
Origin: https://evil.example.com

{"mail":"cracked@example.com"}
徳丸浩のウェブセキュリティ講座

© 2024 Hiroshi Tokumaru

31

32.

クロスサイト・リクエストフォージェリの影響と対策 • 影響を受けるサイト – Cookieでセッション管理を行っているサイト – リクエストヘッダにトークンをセットしているサイトはCSRFの影響はない • 影響 – 脆弱性のある機能を第三者に強制される • パスワードや設定の変更 • 勝手な投稿、変更、削除 • 物品購入 • 対策 – 方法1 トークン(Token)による方法(推奨) ※ アプリケーションフレームワークのCSRF対策機能を素直に使う – 方法2 パスワード確認(再認証) – 保険的対策:重要な処理実行後に登録済みメールアドレスに通知を送付する 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 32

33.

JavaScriptによる検証はバイパスできる 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 33

34.

悪用例: クライアントによるチェック処理の回避 START APIによる権限 チェック APIによる処理でも、チェック と実行(更新等)が別だと、 チェック処理は回避可能 No OK? Yes 処理実行(API) エラー END 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 34

35.

パスワード変更機能の場合 example.jp パスワード変更 旧パスワード 新パスワード 新パスワード(確認) ***** ***** ***** 変更 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 現在パスワード の確認 確認用の再入力 35

36.

パスワード変更機能の脆弱な再認証の実装 利用者 www.example.com POST /auth HTTP/1.1 {"currentPass":"P@assword"} example.jp // 再認証に成功したら // パスワードを変更する if (authCheck()) { changePassword() } else { error("Auth Error") } HTTP/1.1 200 OK POST /changepassword HTTP/1.1 {"newPass":"p@ssw0rd"} HTTP/1.1 200 OK 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru パスワード変更 36

37.

脆弱な再認証に対するXSS攻撃 攻撃者 利用者 www.example.com XSS攻撃 POST /auth HTTP/1.1 {"currentPass":"P@assword"} example.jp // 再認証に成功したら // パスワードを変更する if (authCheck()) { changePassword() } else { error("Auth Error") } HTTP/1.1 200 OK POST /changepassword HTTP/1.1 {"newPass":"cracked"} HTTP/1.1 200 OK 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru パスワード変更 37

38.

クロスサイトスクリプティング(XSS) 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 38

39.
[beta]
ReactでinnerHTML相当の機能を使って改行処理
const App = () => {
return <span dangerouslySetInnerHTML={{__html : nl2br(param)}}></span>
}
const newlineRegex = /(¥n)/g
const nl2br = str => str.replace(newlineRegex, '<br/>')

// 改行を<br>に変換

• Reactの場合、innerHTML相当の機能はdangerouslySetInnerHTMLと
いう見るからに危険そうな名前のディレクティブ
• それでも使ってしまう開発者はいる

徳丸浩のウェブセキュリティ講座

© 2024 Hiroshi Tokumaru

39

40.
[beta]
JSXを活用して改行表示
const App = () => {
return <span>{ nl2br(param) }</span>
}
const newlineRegex = /(¥r¥n|¥r|¥n)/g
const nl2br = (str) =>
str.split(newlineRegex).map((line, index) =>
line.match(newlineRegex) ?
<br key={index}/> :
line
)

徳丸浩のウェブセキュリティ講座

© 2024 Hiroshi Tokumaru

文字列 ABC¥nDEF

配列 ABC

配列 "ABC"

¥n

DEF

<br key=0/>

"DEF"

DOM span
ABC
br
DEF

40

41.

XSSについてのまとめ • XSS対策はとにかくややこしい – 「ややこしくない」と思っている貴方、危険かも? • XSSに関してはCookieのSameSite属性やサードパーティクッキー廃止 は関係ない – 攻撃は同一オリジンからくる! • DOMを正しく構成することが基本(innerHTML禁止) • コンテンツセキュリティポリシー(CSP)で対策(緩和)しましょう 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 41

42.

総まとめ • 以下がWebセキュリティに及ぼす影響を解説 – サードパーティクッキー廃止 – クッキーのSameSite属性 – CORS • ブラウザの進化は安全方向に進んでいるので、過度に心配する必要は ない • CORSやSameSite属性は意味を理解して正しく設定しよう • CSRFは攻撃がしにくくなっているが、Cookieでセッション管理してい る限りはトークンによる対策をしよう 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 42

43.

ご清聴ありがとうございました Any Questions? 徳丸浩のウェブセキュリティ講座 © 2024 Hiroshi Tokumaru 43