---
title: SPAセキュリティ超入門
tags:  #security #php #javascript  
author: [徳丸 浩](https://www.docswell.com/user/ockeghem)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/DEYPZD19EM.jpg?width=480
description: SPA(Single Page Application)の普及が一層進んでおり、従来型のMPAを知らないウェブ開発者も生まれつつあるようです。SPA対応のフレームワークでは基本的な脆弱性については対策機能が用意されていますが、それにも関わらず、脆弱性診断等で基本的な脆弱性が指摘されるケースはむしろ増えつつあります。 本セッションでは、LaravelとReactで開発したアプリケーションをモデルとして、SQLインジェクション、クロスサイトスクリプティング、認可制御不備等の脆弱性の実例を紹介しながら、現実的な対策について紹介します。LaravelやReact以外のフレームワーク利用者にも役立つ説明を心がけます。 PHPカンファレンス2022での講演資料です。  PHPカンファレンスでの動画URL https://www.youtube.com/watch?v=jZ6sWyGxcCs
published: September 25, 22
canonical: https://www.docswell.com/s/ockeghem/K2PPNK-phpconf2022
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/DEYPZD19EM.jpg)

SPAセキュリティ超入門
EGセキュアソリューションズ株式会社
徳丸 浩
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru


# Page. 2

![Page Image](https://bcdn.docswell.com/page/VJN5362D78.jpg)

徳丸浩の自己紹介
• 経歴
– 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
– 技術士（情報工学部門）
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
2


# Page. 3

![Page Image](https://bcdn.docswell.com/page/YE9Y9L18J3.jpg)

本日お話したいこと
• SPA(Single Page Application)とは
• SPAと攻撃経路
• JavaScriptによる検証はバイパスできる
• 認可制御不備
• SQLインジェクション
• クロスサイトスクリプティング(XSS)
• クロスサイトリクエストフォージェリ(CSRF)
• まとめ
※ コード例には、ReactとLaravelを用います
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
3


# Page. 4

![Page Image](https://bcdn.docswell.com/page/GE869X4Z7D.jpg)

SPA(Single Page Application)とは?
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
4


# Page. 5

![Page Image](https://bcdn.docswell.com/page/LEL9W861JR.jpg)

SPA以前のウェブ=MPA(Multi-Page Application)
JavaScriptで代入した値は次のページではリセットされる
→ セッション管理の機能によりデータを引き継ぐ
処理
徳丸浩のウェブセキュリティ講座
こんにちは
次へどうぞ
NEXT
NEXT
処理
© 2022 Hiroshi Tokumaru
ありがとうござい
ました
NEXT
処理
5


# Page. 6

![Page Image](https://bcdn.docswell.com/page/4JMZ9655EW.jpg)

SPA（Single Page Application）の構造
ページ遷移をしないのでJavaScriptの
変数は保持される。
ただし、ページ遷移、戻る、リロー
ドで変数の値はリセットされる
SPA
→ セッション管理あるいは
localStorageによりデータを引き継ぐ
HTML
JS
XHR/
Fetch
コンテンツ
配信
Webサーバー
徳丸浩のウェブセキュリティ講座
JSON
処理
APIサーバー
© 2022 Hiroshi Tokumaru
6


# Page. 7

![Page Image](https://bcdn.docswell.com/page/PJR6GP3Z79.jpg)

SPAのHTML（いわゆるガワ）はとてもシンプル
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
クライアントアプリ
&lt;link rel=&quot;manifest&quot; href=&quot;/manifest.json&quot; /&gt;
の実体はJavaScript
&lt;title&gt;React App&lt;/title&gt;
&lt;script defer=&quot;defer&quot; src=&quot;/static/js/main.js&quot;&gt;&lt;/script&gt;
&lt;link href=&quot;/static/css/main.css&quot; rel=&quot;stylesheet&quot; /&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;noscript&gt;You need to enable JavaScript to run this app.&lt;/noscript&gt;
&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
&lt;/body&gt;
HTMLの実体は
div要素一つだけ
&lt;/html&gt;
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
7


# Page. 8

![Page Image](https://bcdn.docswell.com/page/PEXMX3417X.jpg)

つまり、SPAのクライアント側はほぼ
JavaScriptと言っても過言ではない
（CSSや画像はあります）
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
8


# Page. 9

![Page Image](https://bcdn.docswell.com/page/3EKGWYZMJD.jpg)

ブラウザに表示されるHTMLはどこから?
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
9


# Page. 10

![Page Image](https://bcdn.docswell.com/page/L73N19D275.jpg)

DOMでHTMLを作ります
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
10


# Page. 11

![Page Image](https://bcdn.docswell.com/page/87DWXGP6JG.jpg)

DOMとは?
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;HTMLサンプル&lt;/title&gt;
Document
html
テキストノード
head
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;今日（9月25日）は何の日?&lt;/h1&gt;
属性ノード
title
HTMLサンプル
&lt;p id=&quot;p0925&quot;&gt;
10円カレーの日&lt;br /&gt;
藤ノ木古墳記念日&lt;br /&gt;
山田邦子の日
body
h1
今日（8月11日）は何の日?
&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
p
id
10円カレーの日
br
今日（9月25日）は何の日?
藤ノ木古墳記念日
10円カレーの日
藤ノ木古墳記念日
山田邦子の日
徳丸浩のウェブセキュリティ講座
要素ノード
br
山田邦子の日
© 2022 Hiroshi Tokumaru
11


# Page. 12

![Page Image](https://bcdn.docswell.com/page/VJP9P31ZE8.jpg)

JavaScriptによるDOM操作の基本（Vanilla JS）
&lt;html&gt;
&lt;body&gt;
&lt;div id=&quot;div1&quot;&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;script&gt;
const div1 = document.getElementById(&#039;div1&#039;)
// p要素生成
const p = document.createElement(&#039;p&#039;)
// テキストノード生成
const txt = document.createTextNode(&#039;バナナ&#039;)
// テキストノードをp要素にぶら下げる
p.appendChild(txt)
// p要素をdiv要素にぶら下げる
div1.appendChild(p)
&lt;/script&gt;
&lt;/html&gt;
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
12


# Page. 13

![Page Image](https://bcdn.docswell.com/page/2EVM24YMEQ.jpg)

Reactの表示はJSXが一般的
const SomeApp = () =&gt; {
return (
&lt;div&gt;{ JavaScriptの式 }&lt;/div&gt;
JSの中でHTMLタグが使える
)
}
• JSXはトランスパイラ（Babel）により通常のJavaScriptに変換され
る
• JSX中では { } の中にJavaScriptの式（文字列）を書くと、式の値
が展開されて表示される。HTMLエスケープは必要ない
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
13


# Page. 14

![Page Image](https://bcdn.docswell.com/page/57GGR19X7L.jpg)

SPAと攻撃経路
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
14


# Page. 15

![Page Image](https://bcdn.docswell.com/page/4EQXVDW57P.jpg)

SPAの構造
TOKEN or
SESSION ID
Front End
Store
{ … } JSON
API
{ … } JSON
ビジネスロジック
表示ロジック
DB
localStorage
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
Session
Storage
15


# Page. 16

![Page Image](https://bcdn.docswell.com/page/KJ46MZKV71.jpg)

SPAのセッション管理方式 1 セッションID方式
POST /login
{&quot;id&quot;:&quot;bob&quot;,&quot;pass&quot;:&quot;123456&quot;}
SESSID=f8d92b0…
API
Front End
SESSID=f8d92b0…
Store
ビジネスロジック
{&quot;email&quot;:&quot;bob@example.jp&quot;}
表示ロジック
DB
SESSID=f8d92b0…
5:bob,bob@example.jp
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
Session
Storage
f8d92b0…:5,bob
16


# Page. 17

![Page Image](https://bcdn.docswell.com/page/LE1V8RX47G.jpg)

SPAのセッション管理方式 2: ステートフルトークン
POST /login
{&quot;id&quot;:&quot;bob&quot;,&quot;pass&quot;:&quot;123456&quot;}
token=a94c5d…
API
Front End
token=a94c5d…
Store
ビジネスロジック
{&quot;email&quot;:&quot;bob@example.jp&quot;}
表示ロジック
DB
localStorage
a94c5d…:id=5
5:bob,bob@example.jp
token=a94c5d…
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
17


# Page. 18

![Page Image](https://bcdn.docswell.com/page/GEWLZ1NZ72.jpg)

SPAのセッション管理方式 3 ステートレストークン(JWT等)
POST /login
{&quot;id&quot;:&quot;bob&quot;,&quot;pass&quot;:&quot;123456&quot;}
token=eyJhbG…
API
Front End
Store
token=eyJhbG…
ビジネスロジック
{&quot;email&quot;:&quot;bob@example.jp&quot;}
表示ロジック
デコード&amp;検証
{&quot;sub&quot;:&quot;5&quot;,&quot;exp&quot;:17…}
localStorage
5:bob,bob@example.jp
DB
token=eyJhbG…
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
18


# Page. 19

![Page Image](https://bcdn.docswell.com/page/47ZM1P3LE3.jpg)

Laravel APIのセッション管理
方式名
セッションID
ステートフルトークン
ステートレストークン(JWT)
ブラウザ側保管場所
Cookie
localStorage
localStorage
データの保管場所
ファイルやRedis
DB
トークンに内包
保管できる情報
認証情報 + なんでも
認証情報 + 権限
認証情報 +α
スケーラビリティ
小～中
中
大
即時ログアウト
容易
容易
困難
一斉ログアウト
工夫を要する
容易
困難
Laravelでの実装
Sanctum （SPA認証）
Sanctum（APIトークン）
tymon/jwt-auth
CSRF脆弱性の可能性
あり=対策要
なし
なし
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
19


# Page. 20

![Page Image](https://bcdn.docswell.com/page/YJ6NLMXMJV.jpg)

クライアント側のロジックやデータは取得・変更可能
TOKEN or
SESSION ID
Front End
Store
表示ロジック
{ … }
ビジネスロジック
JSON
DB
localStorage
徳丸浩のウェブセキュリティ講座
API
取得・変更
© 2022 Hiroshi Tokumaru
Session
Storage
20


# Page. 21

![Page Image](https://bcdn.docswell.com/page/GJ551ZXQJ4.jpg)

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


# Page. 22

![Page Image](https://bcdn.docswell.com/page/LE3N19XZE5.jpg)

悪用例1: クライアント側バリデーションの回避
START
HTMLやJavaScritに
よるチェック
&lt;input type=&quot;number&quot; min=&quot;0&quot; max=&quot;100&quot;&gt;
※ 0～100の数値のみ許容
HTMLやJavaScriptによる
チェック（バリデーション
等）は攻撃者は回避可能
No
OK?
Yes
処理実行(API)
エラー
END
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
22


# Page. 23

![Page Image](https://bcdn.docswell.com/page/8EDWXG247G.jpg)

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


# Page. 24

![Page Image](https://bcdn.docswell.com/page/V7P9P31VJ8.jpg)

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


# Page. 25

![Page Image](https://bcdn.docswell.com/page/2JVM24YRJQ.jpg)

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


# Page. 26

![Page Image](https://bcdn.docswell.com/page/5EGGR196EL.jpg)

再認証をバイパスしてパスワード変更が可能
攻撃者
www.example.com
POST /auth HTTP/1.1
{&quot;currentPass&quot;:&quot;P@assword&quot;}
HTTP/1.1 200 OK
$ curl -X POST -H &quot;Content-Type:
application/json&quot; -d &#039;{&quot;newPass&quot;:&quot;123456&quot;}
https://www.example.com/changepassword
POST /changepassword HTTP/1.1
{&quot;newPass&quot;:&quot;123456&quot;}
HTTP/1.1 200 OK
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
26


# Page. 27

![Page Image](https://bcdn.docswell.com/page/4JQXVDW2EP.jpg)

認可制御不備
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
27


# Page. 28

![Page Image](https://bcdn.docswell.com/page/K746MZKPE1.jpg)

認可制御不備の典型的パターン(1)
利用者
http://example.jp/login
http://example.jp/logindone
ログインしました
ID
パスワード
ログイン
秘密情報をどうぞ
情報リソースのURLを知っていると
認証なしで情報が閲覧できる
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
攻撃者
http://example.jp/secret
秘密情報
・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・
URL を知っていると
未ログインでも閲覧可能
28


# Page. 29

![Page Image](https://bcdn.docswell.com/page/LJ1V8RXXEG.jpg)

APIの認可制御不備（正常系）
利用者
example.jp
POST /login HTTP/1.1
example.jp
&lt;script&gt;
xhr = new XMLHttpRequest()
xhr.open(&#039;POST&#039;, &#039;/user&#039;)
xhr.send(…)
&lt;/script&gt;
HTTP/1.1 200 OK
Set-Cookie: SESSID=xxxx
GET /secret HTTP/1.1
Cookie: SESSID=xxxx
HTTP/1.1 200 OK
{&quot;text&quot;:&quot;Secret Information…&quot;}
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
29


# Page. 30

![Page Image](https://bcdn.docswell.com/page/GJWLZ1NKE2.jpg)

APIの認可制御不備（攻撃）
攻撃者
example.jp
POST /login HTTP/1.1
HTTP/1.1 200 OK
Set-Cookie: SESSID=xxxx
$ curl https://example.jp/secret
{&quot;text&quot;:&quot;Secret Information…&quot;}
GET /secret HTTP/1.1
&lt;Cookie なし&gt;
HTTP/1.1 200 OK
{&quot;text&quot;:&quot;Secret Information…&quot;}
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
認証チェック
なしに秘密情
報を返す
30


# Page. 31

![Page Image](https://bcdn.docswell.com/page/4EZM1P3NJ3.jpg)

Laravel/Sanctum の認証設定
// routes/api.php
脆弱なルーティング設定
Route::get(&#039;secret&#039;, [SecretController::class, &#039;secret&#039;]);
// Laravel/Sanctumで認証を要求するルーティング設定例(routes/api.php)
Route::middleware(&#039;auth:sanctum&#039;)-&gt;group(function(){
Route::get(&#039;secret&#039;, [SecretController::class, &#039;secret&#039;]);
});
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
31


# Page. 32

![Page Image](https://bcdn.docswell.com/page/Y76NLMX97V.jpg)

認可制御不備の典型的パターン(2)
ID:yamadaでログイン
ユーザ情報
http://example.jp/profile.php?id=100246
ようこそ yamadaさん
ユーザID:yamada
メールアドレス:xx
氏名: 山田〇〇
電話: 03-xxxx-xxxx
情報リソースのIDを変更する
と権限外の情報が参照できる
http://example.jp/profile.php?id=100247
ようこそ yamadaさん
ユーザID:sato
メールアドレス:xx
氏名: 佐藤△△
電話: 06-xxxx-xxxx
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
内部ID
外部ID
100246
yamada
100247
sato
※アプリケーション内部で
は表示用の外部IDではな
く、内部ID(一連番号)で
管理されている
32


# Page. 33

![Page Image](https://bcdn.docswell.com/page/G7551ZXD74.jpg)

APIの認可制御不備（正常系）
利用者
example.jp
GET /tasks HTTP/1.1
Cookie: SESSID=xxxx
example.jp
タスク一覧
• 3
• 5
• 7
• 10
{&quot;tasks&quot;:[3,5,7,10]}
GET /tasks/10 HTTP/1.1
Cookie: SESSID=xxxx
HTTP/1.1 200 OK
{&quot;id&quot;:10,&quot;user_id&quot;:5,&quot;subject&quot;:&quot;Secret Task&quot;}
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
33


# Page. 34

![Page Image](https://bcdn.docswell.com/page/9J2N1RQMER.jpg)

APIの認可制御不備（攻撃）
攻撃者
example.jp
GET /tasks HTTP/1.1
Cookie: SESSID=zzzz
example.jp
タスク一覧
• 2
• 4
• 6
{&quot;tasks&quot;:[2,4,6]}
GET /tasks/10 HTTP/1.1
Cookie: SESSID=zzzz
HTTP/1.1 200 OK
権限がない情
報が盗まれる
{&quot;id&quot;:10,&quot;user_id&quot;:5,&quot;subject&quot;:&quot;Secret Task&quot;}
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
34


# Page. 35

![Page Image](https://bcdn.docswell.com/page/DEYPZDWPEM.jpg)

ポリシーによる認可制御の実装例(Laravel)
// app/Policies/TaskPolicy.php
class TaskPolicy
{
use HandlesAuthorization;
public function view(User $user, Task $task)
// showコントローラに対応するポリシー
{
return $user-&gt;id === $task-&gt;user_id;
// タスクの持ち主のみがアクセスできる
}
}
// コントローラの定義
class TaskController extends Controller
{
public function show(Task $task)
{
$this-&gt;authorize($task); // ポリシーの確認
return $task;
}
}
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
35


# Page. 36

![Page Image](https://bcdn.docswell.com/page/VJN5369M78.jpg)

認可制御不備の典型的パターン(3)
正常系
一般向けメニュー
http://example.jp/a001
管理者としてログイン
http://example.jp/menu
投稿
閲覧
プロフィール
攻撃
一般ユーザとしてログイン
http://example.jp/menu
一般機能
一般機能
管理者機能
管理者用メニュー
http://example.jp/b001
ユーザ登録
アカウント停止
ロック解除
問い合わせ対応
URLを直接指定すると
管理者用メニューが
表示され実行もできる
一般向けメニュー
http://example.jp/a001
投稿
閲覧
プロフィール
管理者用メニュー
http://example.jp/b001
ユーザ登録
アカウント停止
ロック解除
問い合わせ対応
URL
書き換え
メニューの表示・非表示のみで制御している
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
36


# Page. 37

![Page Image](https://bcdn.docswell.com/page/YE9Y9L2WJ3.jpg)

APIの認可制御不備（正常系）
管理者
example.jp
POST /login HTTP/1.1
example.jp
&lt;script&gt;
xhr = new XMLHttpRequest()
xhr.open(&#039;POST&#039;, &#039;/user&#039;)
xhr.send(…)
&lt;/script&gt;
HTTP/1.1 200 OK
Set-Cookie: SESSID=xxxx
{&quot;id&quot;:1}
PATCH /users/10 HTTP/1.1
Cookie: SESSID=xxxx
{&quot;email&quot;:&quot;carol@example.jp&quot;}
HTTP/1.1 200 OK
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
管理者がメールア
ドレスを変更する
37


# Page. 38

![Page Image](https://bcdn.docswell.com/page/GE869X5R7D.jpg)

APIの認可制御不備（攻撃）
攻撃者（一般ユーザー）
example.jp
POST /login HTTP/1.1
example.jp
&lt;script&gt;
xhr = new XMLHttpRequest()
xhr.open(&#039;POST&#039;, &#039;/user&#039;)
xhr.send(…)
&lt;/script&gt;
HTTP/1.1 200 OK
Set-Cookie: SESSID=zzzz
{&quot;id&quot;:6}
PATCH /users/10 HTTP/1.1
Cookie: SESSID=zzzz
※一般ユーザーからは呼び出されないエンドポイント
{&quot;email&quot;:&quot;carol@example.jp&quot;}
HTTP/1.1 200 OK
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
権限のないメール
アドレス変更
38


# Page. 39

![Page Image](https://bcdn.docswell.com/page/LEL9W8Y2JR.jpg)

認可制御不備の典型的パターン(4)
正常系
一般ユーザとしてログイン
Cookie: USER=normal
管理者としてログイン
Cookie: USER=admin
攻撃
投稿
閲覧
一般ユーザとしてログイン
Cookie: USER=admin
投稿
閲覧
ユーザ登録
アカウント停止
Cookieに保持した権限情報を変
更しただけで管理者メニューが
表示され、実行もできる
投稿
閲覧
ユーザ登録
アカウント停止
フレームワークのStoreやlocalStorage、クッキーに権限情報を保持している
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
39


# Page. 40

![Page Image](https://bcdn.docswell.com/page/4JMZ96N9EW.jpg)

APIの認可制御不備（正常系）
管理者
example.jp
POST /login HTTP/1.1
localStorage
HTTP/1.1 200 OK
Set-Cookie: SESSID=xxxx
{&quot;id&quot;:1, &quot;role&quot;:1}
role:1
PATCH /users/10 HTTP/1.1
Cookie: SESSID=xxxx
{&quot;role&quot;:1, &quot;email&quot;:&quot;carol@example.jp&quot;}
HTTP/1.1 200 OK
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
管理者がメールア
ドレスを変更する
40


# Page. 41

![Page Image](https://bcdn.docswell.com/page/PJR6GPD979.jpg)

APIの認可制御不備（攻撃）
攻撃者（一般ユーザー）
example.jp
POST /login HTTP/1.1
localStorage
HTTP/1.1 200 OK
Set-Cookie: SESSID=zzzz
{&quot;id&quot;:6, &quot;role&quot;:0}
role:0
role:1
PATCH /users/10 HTTP/1.1
Cookie: SESSID=zzzz
{&quot;role&quot;:1, &quot;email&quot;:&quot;carol@example.jp&quot;}
HTTP/1.1 200 OK
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
権限のないメール
アドレス変更
41


# Page. 42

![Page Image](https://bcdn.docswell.com/page/PEXMX3137X.jpg)

Gateファサードによる認可実装例(Laravel)
// app/Providers/AuthServiceProvider.php
class AuthServiceProvider extends ServiceProvider
{
/* */
public function boot()
{
$this-&gt;registerPolicies();
Gate::define(&#039;admin&#039;, function ($user) { // adminポリシーの定義
return $user-&gt;role &gt;= 1; // ユーザのroleが1以上をadminとする
});
}
}
// routes/api.php
ルーティング設定
// ...
Route::group([&#039;middleware&#039; =&gt; [&#039;auth&#039;, &#039;can:admin&#039;]], function () {
// adminのみupdateEmail を許可する
Route::patch(&#039;user/update_email/{user}&#039;, [UserController::class, &#039;updateEmail&#039;]);
});
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
42


# Page. 43

![Page Image](https://bcdn.docswell.com/page/3EKGWY2NJD.jpg)

認可制御の正しい実装
• 認可制御の基本は、APIの中で権限を確認すること
– 秘密情報の表示
– 権限の必要な機能実行
– その他、権限を必要とする操作
• 権限を確認する基準は、セッション変数やトークンに紐づいた
ログインID
• admin等の特権専用ユーザーは作らず、個人IDに権限を割り当てる仕
様が好ましい
• 複雑な認可制御が必要な場合は、役割を抽象化したロールを定義する
• 時間経過に伴い、権限やロールが変化する場合もあるので、権限や
ロールはセッション変数に保存せず、データベースを都度確認する
• ロールと権限を「権限マトリックス」としてドキュメント化する
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
43


# Page. 44

![Page Image](https://bcdn.docswell.com/page/L73N194Z75.jpg)

ロールと権限マトリックスの例
ロール
権限
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
44


# Page. 45

![Page Image](https://bcdn.docswell.com/page/87DWXGQ4JG.jpg)

SQLインジェクション
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
45


# Page. 46

![Page Image](https://bcdn.docswell.com/page/VJP9P3LVE8.jpg)

LaravelのEloquent / クエリビルダー は基本的に堅牢
• LaravelのORマッパ(Eloquent)やクエリビルダーは通常何もしなくても
SQLインジェクション対策されている
– $tasks = Task::where(&#039;kind&#039;, &#039;=&#039;, $kind)-&gt;get();
… WHERE kind=?
プレースホルダでSQLイン
ジェクション対策される
– $tasks = Task::where(&#039;kind = 1 or 1=1#&#039;, &#039;=&#039;, $kind)-&gt;get();
Column not found とい
うエラーになる
– $tasks = Task::where(&#039;kind&#039;, &#039;=1 OR true# &#039;, $kind)-&gt;get();
kind = &#039;=1 OR true#&#039;
と解釈される
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
… WHERE kind=&#039;=1 OR true#&#039;
46


# Page. 47

![Page Image](https://bcdn.docswell.com/page/2EVM24QREQ.jpg)

Laravel（Eloquent）のwhereRawメソッドの用途と注意点
• SQLのWHERE句を「生で」指定できる
• 文字列連結でWHERE句を組み立てると、普通にSQLインジェクション
脆弱となる
• 正しくはプレイスホルダを使う（後述）
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
47


# Page. 48

![Page Image](https://bcdn.docswell.com/page/57GGR1W67L.jpg)

脆弱性のあるアプリケーション（正常系）
class TaskController extends Controller
{
public function getTaks(Request $request)
{
kind=Shopping
$kind = $request-&gt;kind;
$tasks = Task::whereRaw(&quot;kind = BINARY &#039;$kind&#039;&quot;)-&gt;get();
return $tasks;
大文字・小文字
}
を区別して検索
[
}
{
select * from `tasks` where kind = BINARY &#039;Shopping&#039;
&quot;id&quot;:2,
&quot;kind&quot;:&quot;Shopping&quot;,
&quot;title&quot;:&quot;Remember the milk&quot;,
&quot;memo&quot;:&quot;Get one milk. If the eggs are there, get six.&quot;
},
【以下略】
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
48


# Page. 49

![Page Image](https://bcdn.docswell.com/page/4EQXVD327P.jpg)

脆弱性のあるアプリケーション（攻撃）
class TaskController extends Controller
{
public function getTaks(Request $request)
kind=&#039; UNION SELECT id, name, email, password, FROM users #
{
$kind = $request-&gt;kind;
$tasks = Task::whereRaw(&quot;kind = BINARY &#039;$kind&#039;&quot;)-&gt;get();
return $tasks;
}
select * from `tasks` where kind = BINARY &#039;&#039; UNION SELECT id, name, email, password FROM users #&#039;
}
[
{
パスワードハッシュ値を
&quot;id&quot;:2,
含む個人情報が漏洩する
&quot;kind&quot;:&quot;takahashi&quot;,
&quot;title&quot;:&quot;takahashi@example.jp&quot;,
&quot;memo&quot;:&quot;$2y$10$wMgTH4/IDTpzuUtRDvkYj.kDbfdnXJ6odWnJ5BvfZCKeu0bTJpFhG&quot;
},
【以下略】
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
49


# Page. 50

![Page Image](https://bcdn.docswell.com/page/KJ46MZ1P71.jpg)

対策 プレイスホルダ
• O/Rマッパーの機能を正しく使うことでSQLインジェクション対策
• whereRaw等を用いる場合は、文字列連結による条件生成をやめて、
プレイスフォルダを用いること
$tasks = Task::whereRaw(&quot;kind = BINARY &#039;$kind&#039;&quot;)-&gt;get();
$tasks = Task::whereRaw(&quot;kind = BINARY ?&quot;, [$kind])-&gt;get();
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
50


# Page. 51

![Page Image](https://bcdn.docswell.com/page/LE1V8RGX7G.jpg)

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


# Page. 52

![Page Image](https://bcdn.docswell.com/page/GEWLZ1KK72.jpg)

SPAに対するXSS攻撃の概要
SESSION ID
JavaScript
Front End
表示ロジック
{ … } JSON
TOKEN
{ … } JSON
DB
localStorage
徳丸浩のウェブセキュリティ講座
API
XSSがあると、攻撃者
はAPIを自由に悪用で
き、秘密情報の盗み取
りや不正操作が可能に
ビジネスロジック
なる
© 2022 Hiroshi Tokumaru
Session
Storage
52


# Page. 53

![Page Image](https://bcdn.docswell.com/page/47ZM1PZNE3.jpg)

Reactの表示はJSXが一般的（再掲）
const SomeApp = () =&gt; {
return (
&lt;div&gt;{ JavaScriptの式 }&lt;/div&gt;
JSの中でHTMLタグが使える
)
}
• JSXはプリプロセッサ（Babel）により通常のJavaScriptに変換され
る
• JSX中では { } の中にJavaScriptの式（文字列）を書くと、式の値
が展開されて表示される。HTMLエスケープは必要ない
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
53


# Page. 54

![Page Image](https://bcdn.docswell.com/page/YJ6NLMZ9JV.jpg)

ここからは、具体的なユースケースとして、
改行混じりのテキスト表示を考えます
～改行を&lt;br&gt;タグに変換する～
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
54


# Page. 55

![Page Image](https://bcdn.docswell.com/page/GJ551ZWDJ4.jpg)

なぜ改行を&lt;br&gt;タグに変換するのか
• HTMLのテキスト上は改行は空白として扱われる
&lt;p&gt;AAA
BBB&lt;/p&gt;
AAA BBB
と表示される
• 改行するには、改行文字を&lt;br&gt;タグに変換する
&lt;p&gt;AAA&lt;br&gt;BBB&lt;/p&gt;
AAA
BBB
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
55


# Page. 56

![Page Image](https://bcdn.docswell.com/page/9E2N1R2M7R.jpg)

PHPの場合
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
56


# Page. 57

![Page Image](https://bcdn.docswell.com/page/D7YPZDYP7M.jpg)

PHPで改行交じりテキストを表示するのは簡単
• Vanilla PHP（素のPHP）の場合
echo nl2br(htmlspecialchars($text));
まずHTMLエスケープしてから
改行を&lt;br&gt;に変換する
• Laravel（Blade）の場合
{!! nl2br(e($text)) !!}
まずHTMLエスケープしてから
改行を&lt;br&gt;に変換する
{!! !!} を用いてBladeのエスケープを抑止
• SPAの場合は上記は使えないのでJavaScriptで対応する必要がある
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
57


# Page. 58

![Page Image](https://bcdn.docswell.com/page/VEN536PMJ8.jpg)

Vanilla JSでinnerHTMLを使う（危険）
&lt;div&gt;
&lt;span id=&quot;span1&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;script&gt;
const span1 = document.getElementById(&#039;span1&#039;)
span1.innerHTML = text.replace(&#039;¥n&#039;, &#039;&lt;br&gt;&#039;)
&lt;/script&gt;
• innerHTMLを使うと改行→&lt;br&gt;の変換は簡単だが、DOM Base XSS脆弱
性が混入する
• それでも使ってしまう開発者はいる
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
58


# Page. 59

![Page Image](https://bcdn.docswell.com/page/Y79Y9L3WE3.jpg)

innerHTMLによるXSS
&lt;html&gt;
&lt;body&gt;
&lt;div id=&quot;div1&quot;&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;script&gt;
const div1 = document.getElementById(&#039;div1&#039;)
div1.innerHTML = &#039;&lt;img src=1 onerror=alert(1)&gt;&#039;
&lt;/script&gt;
&lt;/html&gt;
徳丸浩のウェブセキュリティ講座
Copyright © Hiroshi Tokumaru
59


# Page. 60

![Page Image](https://bcdn.docswell.com/page/G7869XMRJD.jpg)

Reactの場合
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
60


# Page. 61

![Page Image](https://bcdn.docswell.com/page/L7L9W832ER.jpg)

ReactでinnerHTML相当の機能を使う
const App = () =&gt; {
return &lt;span dangerouslySetInnerHTML={{__html : nl2br(param)}}&gt;&lt;/span&gt;
}
const newlineRegex = /(¥n)/g
const nl2br = str =&gt; str.replace(newlineRegex, &#039;&lt;br/&gt;&#039;)
// 改行を&lt;br&gt;に変換
• Reactの場合、innerHTML相当の機能はdangerouslySetInnerHTMLと
いう見るからに危険そうな名前のディレクティブ
• それでも使ってしまう開発者はいる
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
61


# Page. 62

![Page Image](https://bcdn.docswell.com/page/4EMZ96D97W.jpg)

定番の &lt;script&gt;alert(1)&lt;/script&gt;を試すと…
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
62


# Page. 63

![Page Image](https://bcdn.docswell.com/page/PER6GP49J9.jpg)

ReactアプリのDOM Based XSSの例
innerHTMLでscript要素
を追加しても発火しない
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
63


# Page. 64

![Page Image](https://bcdn.docswell.com/page/P7XMX323JX.jpg)

&lt;img src=/ onerror=alert(1)&gt; を試す
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
64


# Page. 65

![Page Image](https://bcdn.docswell.com/page/37KGWYMNED.jpg)

ReactアプリのDOM Based XSSの例
onerrorイベント等であ
れば発火する
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
65


# Page. 66

![Page Image](https://bcdn.docswell.com/page/LJ3N19YZJ5.jpg)

このような声も…
https://twitter.com/azu_re/status/1570780991614222337
徳丸浩のウェブセキュリティ講座
66


# Page. 67

![Page Image](https://bcdn.docswell.com/page/8JDWXG54EG.jpg)

JSXを活用して改行表示
const App = () =&gt; {
return &lt;span&gt;{ nl2br(param) }&lt;/span&gt;
}
const newlineRegex = /(¥r¥n|¥r|¥n)/g
const nl2br = (str) =&gt;
str.split(newlineRegex).map((line, index) =&gt;
line.match(newlineRegex) ?
&lt;br key={index}/&gt; :
line
)
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
文字列 ABC¥nDEF
配列 ABC
配列 &quot;ABC&quot;
¥n
DEF
&lt;br key=0/&gt;
&quot;DEF&quot;
DOM span
ABC
br
DEF
67


# Page. 68

![Page Image](https://bcdn.docswell.com/page/VEP9P3GV78.jpg)

javascriptスキームによるXSS
const App = () =&gt; {
return &lt;div&gt;
&lt;a href={url}&gt;リンク&lt;/a&gt;
&lt;/div&gt;
urlとしてjavascript:alert(1)
をセットすると…
}
リンクをクリック
すると発火する
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
68


# Page. 69

![Page Image](https://bcdn.docswell.com/page/27VM246R7Q.jpg)

【参考】AngularでDOM Based XSSに対してサニタイズが入る
app.component.html
&lt;div&gt;&lt;span [innerHTML]=&quot;text&quot;&gt;&lt;/span&gt;&lt;/div&gt;
import { Component } from &#039;@angular/core&#039;
@Component({
selector: &#039;app-root&#039;,
templateUrl: &#039;./app.component.html&#039;,
})
export class AppComponent {
text = &#039;&lt;img src=/ onerror=alert(1)&gt;&#039;;
}
app.component.ts
onerrorイベントが
削除されている
&lt;img src=&quot;/&quot;&gt;
徳丸浩のウェブセキュリティ講座
Copyright © Hiroshi Tokumaru
69


# Page. 70

![Page Image](https://bcdn.docswell.com/page/5JGGR1N6JL.jpg)

XSSの緩和策の変遷
• X-XSS-Protection（XSS Auditor）
–
–
–
–
–
IE8にて初めて導入され、その後Google Chrome、Safariに導入される
Firefoxは一貫して導入しなかった
Google Chrome 78にて無効化（2019年10月）
Safari 15.4にて無効化（2022年3月）
現在X-XSS-Protectionに対応しているブラウザはない
• Content Security Policy(CSP)
– X-XSS-Protectionに代わるXSS緩和策としてMozillaにより提唱され、その後標
準化された
– Google Chrome 25（2013年2月）、Firefox 23（2013年 8月）、Safari 7（2013
年10月）にて正式導入
– 現在および今後のXSS緩和策はこちら
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
70


# Page. 71

![Page Image](https://bcdn.docswell.com/page/47QXVDQ2JP.jpg)

Content Security Policy入門
• 以下は、JavaScriptのソースを自分自身のオリジンと api.example.com に限定する
– Content-Security-Policy: script-src &#039;self&#039; api.example.com
• 同時にインラインスクリプトも禁止される
– &lt;script&gt; … &lt;/script&gt; や &lt;body onload=&quot;…&quot;&gt; は禁止される
• インラインスクリプトを書きたい場合は、ナンスまたはハッシュを指定
&lt;script nonce=EDNnf03nceIOfn39fn3e9h3sdfa&gt;
…
&lt;/script&gt;
に対して下記のCSPヘッダ
Content-Security-Policy: script-src &#039;nonce-EDNnf03nceIOfn39fn3e9h3sdfa&#039;
あるいは
&lt;script&gt;alert(&#039;Hello, world.&#039;);&lt;/script&gt;
に対しては下記のCSPヘッダ
Content-Security-Policy: script-src &#039;sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng=&#039;
参考: https://developers.google.com/web/fundamentals/security/csp/?hl=ja
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
71


# Page. 72

![Page Image](https://bcdn.docswell.com/page/KE46MZNPJ1.jpg)

なぜCSPを勧めるか
• MPA（ページ遷移型のウェブアプリケーション）では、CSPの設定が
やっかいである
– インラインスクリプトが使えないのは面倒
• SPAはCSPと相性がよい
– webpack等が生成するコードはHTMLとJavaScriptが分離されており、インライ
ンスクリプトが元々ない
– CSPを利用するサイトが増えている
• XSS Auditorは蓄積型XSSおよびDOM Based XSSに効果がないが、CSP
は防御効果がある
• 開発当初からCSPヘッダを有効にして、確認しながら進めるとよい
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
72


# Page. 73

![Page Image](https://bcdn.docswell.com/page/L71V8R5XJG.jpg)

SPAのXSSまとめ
• SPAのフレームワークはXSS対策がなされている
• innerHTML相当の機能やjavascriptスキームによるXSSの可能性
• XSSの検査する時はscriptタグではなくイベントハンドラを使う
○ &lt;img src=/ onerror=alert(1)&gt;
× &lt;script&gt;alert(1)&lt;/script&gt;
• X-XSS-Protection は今は有効ではない
• CSPのすすめ
• XSSやばい（APIの悪用）ので気をつけよう
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
73


# Page. 74

![Page Image](https://bcdn.docswell.com/page/G7WLZ15KJ2.jpg)

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


# Page. 75

![Page Image](https://bcdn.docswell.com/page/4JZM1PNN73.jpg)

まず被害者は正規サイトにログインしている前提
利用者
example.jp
GET /loginform HTTP/1.1
example.jp
&lt;script&gt;
xhr = new XMLHttpRequest()
xhr.open(&#039;POST&#039;, &#039;/user&#039;)
xhr.send(…)
&lt;/script&gt;
HTTP/1.1 200 OK
Content-Length: 1056
POST /login HTTP/1.1
{&quot;id&quot;:&quot;bob&quot;,&quot;password&quot;:&quot;123456&quot;}
HTTP/1.1 200 OK
Set-Cookie: SESSID=xxxx
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
75


# Page. 76

![Page Image](https://bcdn.docswell.com/page/YE6NLM89EV.jpg)

脆弱なAPI（メールアドレス変更）の正常系
利用者
example.jp
&lt;script&gt;
xhr = new XMLHttpRequest()
xhr.open(&#039;PATCH&#039;, &#039;/user&#039;)
xhr.send(…)
&lt;/script&gt;
example.jp
PATCH /user HTTP/1.1
Cookie: SESSID=xxxx
{&quot;email&quot;:&quot;bob@example.jp&quot;}
HTTP/1.1 200 OK
メールアドレス変更
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
76


# Page. 77

![Page Image](https://bcdn.docswell.com/page/GE551Z9DE4.jpg)

罠サイトからCSRF攻撃する模様
利用者
攻撃者
evil.example.com
example.jp
GET /trap HTTP/1.1
evil.example.com
&lt;form action=
&quot;https://example.jp/user&quot;
method=&quot;post&quot;&gt;
&lt;input &quot;_method&quot;=&quot;patch&quot;
&lt;input name=&quot;email&quot;
value=&quot;evil@example.com&quot;&gt;
&lt;input type=&quot;submit&quot;&gt;
&lt;/form&gt;
HTTP/1.1 200 OK
Content-Length: 832
POST /user HTTP/1.1
Cookie: SESSID=xxxx
_method=PATCH&amp;email=evil@example.com
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
77


# Page. 78

![Page Image](https://bcdn.docswell.com/page/972N1R5MJR.jpg)

Laravel / Sanctum のCSRFに関するサマリ
• CSRF脆弱性が問題になるのはCookieによるセッション管理の場合のみ
– Autherizationヘッダにトークンをセットする場合は影響なし
• LaravelのCSRF対策はCSRFトークン
• SanctumはCookieのSameSite属性を強制的に Lax にセットする
• Laravelを普通に使えばCSRFは心配ない
– Cookieによるセッション管理を使う場合はCSRF対策を無効化しないこと
• 色々理屈をつけて無効化する人がいるがお勧めしない
• Laravel以外のフレームワークを使う場合は、フレームワークのCSRF対
策機能を調べて使う
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
78


# Page. 79

![Page Image](https://bcdn.docswell.com/page/DJYPZDKPJM.jpg)

総まとめ
• SPAの代表的な脆弱性を紹介
• 脆弱性診断ではSPAの脆弱性が多く見つかっている
– 特に、認可制御不備とSQLインジェクション
• ブラウザ側（HTMLとJavaScript）のチェック処理は回避できる
• フレームワークのセキュリティ機能を正しく活用する
• innerHTML相当の機能（dangerouslySetInnerHTML、v-html等）は原則
使わない
– 使う場合は危険な要素や属性を取り除くサニタイズ処理を行う
• 開発者ができるセキュリティテストは多いので試してみよう
徳丸浩のウェブセキュリティ講座
© 2022 Hiroshi Tokumaru
79


