---
title: 著名PHPアプリの脆弱性に学ぶセキュアコーディングの原則
tags:  #security #php  
author: [徳丸 浩](https://www.docswell.com/user/ockeghem)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/L2EVN1NP7Q.jpg?width=480
description: PHPカンファレンス2017における徳丸浩の講演「著名PHPアプリの脆弱性に学ぶセキュアコーディングの原則」です
published: October 08, 17
canonical: https://www.docswell.com/s/ockeghem/KYVE3K-php-conference-2017
---
# Page. 1

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

著名PHPアプリの脆弱性に学ぶ
セキュアコーディングの原則
EG セキュアソリューションズ株式会社
徳丸 浩


# Page. 2

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

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


# Page. 3

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

脆弱性の分類と&quot;入力値&quot;の関係
3


# Page. 4

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

【参考】CVEとCWE
• 脆弱性の国際的な分類として CVE と CWE がある
• CVE(Common Vulnerabilities and Exposures)
– 個別ソフトウェアの具体的な脆弱性を識別する番号
– 米国政府の支援を受けた非営利団体のMITRE社他のCNAが採番
– CVEの例
• CVE-2017-1001000 WordPress Rest APIの脆弱性
• CVE-2017-5638 Apache Struts2の脆弱性S2-045
• CWE（Common Weakness Enumeration）
– 脆弱性の種類を識別する番号
– MITRE社が中心となって策定
– CWEの例
• CWE-89 SQLインジェクション
• CWE-22 ディレクトリトラバーサル
※ CAN: CVE Numbering Authority, CVE 採番機関
参考: https://www.ipa.go.jp/security/vuln/CVE.html
https://www.ipa.go.jp/security/vuln/CWE.html
4


# Page. 5

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

CWEの脆弱性タイプの階層構造図
https://www.ipa.go.jp/security/vuln/CWE.html より引用
5


# Page. 6

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

CWE-20 不適切な入力確認
6


# Page. 7

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

Java セキュアコーディングスタンダード CERT/Oracle 版
• はじめに
• 00. 入力値検査とデータの
無害化 (IDS)
• 01. 宣言と初期化 (DCL)
• 02. 式 (EXP)
• 03. 数値型とその操作
(NUM)
• 04. オブジェクト指向 (OBJ)
• 05. メソッド (MET)
• 06. 例外時の動作 (ERR)
• 07. 可視性とアトミック性
(VNA)
• 08. ロック (LCK)
• 09. スレッド API (THI)
• 10. スレッドプール (TPS)
• 11. スレッドの安全性に関
する雑則 (TSM)
• 12. 入出力 (FIO)
• 13. シリアライズ (SER)
• 14. プラットフォームのセ
キュリティ (SEC)
• 15. 実行環境 (ENV)
• 49. 雑則 (MSC)
• AA. 参考情報
• BB. Glossary
https://www.jpcert.or.jp/java-rules/
7


# Page. 8

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

IDS00-J. 信頼境界を越えて渡される信頼できないデータは無害化する
多くのプログラムは、認証済みでないユーザやネットワーク接続等、信頼
できない情報源からデータを受け取り、それを（改変したり、あるいはそ
のまま）信頼境界（trust boundary）を越えて、信頼される側に渡す。多く
の場合、データは、一定のシンタックスを持つ文字列であり、プログラム
内部のサブシステムによって解析される。不正な形式の入力データには対
応できないかもしれないし、インジェクション攻撃が含まれているかもし
れないため、そのような入力データは無害化（sanitize）しなくてはならな
い。
特にコマンドインタプリタやパーサに渡される文字列データはすべて、解
析される文脈で無害な状態（innocuous）にしなければならない。
コマンドインタプリタやパーサの多くは、独自の無害化メカニズムや検
査機構を備えている。可能であれば、それらの無害化メカニズムを使用す
るほうが、独自に無害化メカニズムを実装するよりも好ましい。独自に実
装した無害化メカニズムでは、特殊なケースやパーサの複雑な内部構造に
配慮しない実装を行ってしまう可能性がある。それだけでなく、コマンド
インタプリタやパーサに新しい機能が追加されたとき、無害化メカニズム
が適切にメンテナンスされない恐れもある。
旧版です
アーカイブ http://web.archive.org/web/20150515043831/ https://www.jpcert.or.jp/java-rules/ids00-j.html
8


# Page. 9

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

信頼境界の中で閉じた処理の例（固定のSQL文実行）
信頼境界(Trust Boundary)
SQL実行
SQL文
SQLクエリ
SELECT * FROM employee
プログラムソース
SQL文
SQL DB
潜在的な脆弱性があっても、
外部から攻撃されることはない
9


# Page. 10

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

信頼できない値によりSQLインジェクションになる例
信頼境界(Trust Boundary)
SQLインジェクション
ブラウザ
従業員コード
SQL実行
SELECT * FROM employee
WHERE id=&#039;$id&#039;
SQL文
プログラムソース
SQL文
SQLクエリ
SQL DB
10


# Page. 11

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

ディレクトリトラバーサルの典型例
信頼境界(Trust Boundary)
画面1
hiddenパラメータ
設定ファイル
file=
template.html
ディレクトリトラバーサル
値の改
変
fopen
ファイルシステム
fopen(&#039;../../../etc/passwd&#039;, …
11


# Page. 12

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

認可制御不備
信頼境界(Trust Boundary)
takagiでログイン
Hidden
パラメータ
メニュー画面1
セッション変数
id=takagi
値の改変
satoの個人情報
個人情報表示
ユーザーDB
ブラウザ
認可制御不備
12


# Page. 13

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

セカンドオーダーSQLインジェクション
信頼境界(Trust Boundary)
ブラウザ
従業員コード
氏名
&#039;;DELETE FROM
employee --
SQL実行1
(INSERT)
安全なSQLクエリ
INSERT INTO employee
VALUES(:id, :name, …)
SQL DB
SQL実行2
(SELECT)
氏名
SQL実行1
(UPDATE)
氏名
&#039;;DELETE FROM employee --
脆弱なSQLクエリ
UPDATE employee
SET name=&#039;$name&#039; …
氏名
SQL DB
&#039;;DELETE FROM employee -SQLインジェクション
13


# Page. 14

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

では、SQLインジェクション対策、
どうすればいいか?
14


# Page. 15

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

こうですか、わかりません (&gt;_&lt;)/
信頼境界を越えて渡される信頼できないデータは無害化する
信頼境界
信頼できないデータ
無害化
フィルタ
無害なデータ
SQL
呼び出し
15


# Page. 16

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

こうですか、わかりません (&gt;_&lt;)/
信頼境界を越えて渡される信頼できないデータは無害化する
信頼境界
信頼できないデータ
無害化
フィルタ
無害なデータ
SQL
呼び出し
16


# Page. 17

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

IDS00-J. 信頼境界を越えて渡される信頼できないデータは無害化する
多くのプログラムは、認証済みでないユーザやネットワーク接続等、信頼
できない情報源からデータを受け取り、それを（改変したり、あるいはそ
のまま）信頼境界（trust boundary）を越えて、信頼される側に渡す。多く
の場合、データは、一定のシンタックスを持つ文字列であり、プログラム
内部のサブシステムによって解析される。不正な形式の入力データには対
応できないかもしれないし、インジェクション攻撃が含まれているかもし
れないため、そのような入力データは無害化（sanitize）しなくてはならな
い。
特にコマンドインタプリタやパーサに渡される文字列データはすべて、解
析される文脈で無害な状態（innocuous）にしなければならない。
コマンドインタプリタやパーサの多くは、独自の無害化メカニズムや検
査機構を備えている。可能であれば、それらの無害化メカニズムを使用す
るほうが、独自に無害化メカニズムを実装するよりも好ましい。独自に実
装した無害化メカニズムでは、特殊なケースやパーサの複雑な内部構造に
配慮しない実装を行ってしまう可能性がある。それだけでなく、コマンド
インタプリタやパーサに新しい機能が追加されたとき、無害化メカニズム
が適切にメンテナンスされない恐れもある。
アーカイブ http://web.archive.org/web/20150515043831/ https://www.jpcert.or.jp/java-rules/ids00-j.html
17


# Page. 18

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

IDS00-J. 信頼境界を越えて渡される信頼できないデータは無害化する
違反コード
以下の違反コード例は、ユーザ認証を行うJDBCのコードを示している。パ
スワードはchar型配列として渡され、データベースへの接続が作成され、
パスワードがハッシュ化されている。
残念ながらこのコードはSQLインジェクション攻撃を許してしまう。SQL文
sqlString は無害化されていない入力値を受け付けており、前述の攻撃シナ
リオが成立してしまうだろう。
適合コード (PreparedStatement)
幸いJDBCライブラリはSQLコマンドを組み立てるAPIを提供しており、信頼
できないデータを無害化してくれる。java.sql.PreparedStatementクラスは
入力文字列を適切にエスケープ処理するため、適切に利用すればSQLイン
ジェクション攻撃を防ぐことができる。これはコンポーネントベースで行
う無害化の一例である。
この適合コードでは java.sql.Statement の代わりに PreparedStatementを使
用するように doPrivilegedAction() メソッドを変更している。また、引数
username の長さを検証しており、攻撃者が任意に長いユーザ名を送り込む
ことを防止している。
18
カイブ http://web.archive.org/web/20150515043831/ https://www.jpcert.or.jp/java-rules/ids00-j.html


# Page. 19

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

信頼できない値を安全に処理する例（プレースホルダ）
信頼境界(Trust Boundary)
ブラウザ
従業員コード
SQL実行
SELECT * FROM employee
WHERE id=:id
SQL文
プログラムソース
SQL文
SQLクエリ
SQL DB
19


# Page. 20

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

簡単にすると、こうだった
信頼境界を越えて渡される信頼できないデータは無害化する
信頼境界
プレースホルダ
信頼できないデータ
安全な形でSQL
呼び出し
20


# Page. 21

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

そもそも信頼境界関係なくね?
21


# Page. 22

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

そう、故に見出しが改定された
22


# Page. 23

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

まさかの「SQLインジェクションを防ぐ」
信頼境界も、信頼できないも、無害化も
タイトルから消えた
https://www.jpcert.or.jp/java-rules/ids00-j.html
23


# Page. 24

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

では、信頼境界は無意味?
24


# Page. 25

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

そうでもない
25


# Page. 26

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

こういうのはダメ
hiddenパラメータでSQL文を渡している
信頼境界
安全に呼び出す方法がない
SQL呼び出し
hiddenパラメータで
SQL文を渡している
26


# Page. 27

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

phpMyAdminの場合
DB管理者
信頼境界
認証・認可
SQL呼び出し
SQL文
信頼できる情報源
SQL文
SQL文
ソースコード
セッション変数
データベース
設定ファイル
…
27


# Page. 28

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

ディレクトリトラバーサルの典型例
信頼境界(Trust Boundary)
画面1
設定ファイル
file=
template.html
Hidden
パラメータ
fopen
ファイルシステム
fopen(&#039;../../../etc/passwd&#039;, …
28


# Page. 29

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

ディレクトリトラバーサルの対策(1)
信頼境界(Trust Boundary)
file=template.html
画面1
設定ファイル
file=
template.html
セッション変数
file=template.html
fopen
ファイルシステム
fopen(&#039;template.html&#039;, …
29


# Page. 30

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

ディレクトリトラバーサルの対策(2)
信頼境界(Trust Boundary)
画面1
設定ファイル
file=
template.html
Hidden
パラメータ
basename
fopen
ファイルシステム
file=passwd
fopen(&#039;passwd&#039;, …
30


# Page. 31

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

認可制御不備
信頼境界(Trust Boundary)
takagiでログイン
Hidden
パラメータ
メニュー画面1
セッション変数
id=takagi
satoの個人情報
個人情報表示
ユーザーDB
ブラウザ
31


# Page. 32

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

認可制御不備の対策(1)
信頼境界(Trust Boundary)
takagiでログイン
セッション変数
id=takagi
ブラウザ
個人情報表
示
takagiの個人情報
ユーザーDB
32


# Page. 33

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

認可制御不備の対策(2)
信頼境界(Trust Boundary)
takagiでログイン
Hidden
パラメータ
メニュー画
面1
認可制御
ブラウザ
セッション変数
id=takagi
個人情報表
示
33


# Page. 34

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

セカンドオーダーSQLインジェクション
信頼境界(Trust Boundary)
ブラウザ
従業員コード
氏名
SQL実行1
(INSERT)
安全なSQLクエリ
INSERT INTO employee
VALUES(:id, :name, …)
SQL DB
&#039;; DELETE FROM
employee --
SQL実行2
(SELECT)
氏名
&#039;; DELETE FROM employee --
氏名
SQL実行1
(UPDATE)
脆弱なSQLクエリ
UPDATE employee
SET name=&#039;&#039;;
DELETE FROM employee --&#039; …
SQL DB
34


# Page. 35

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

セカンドオーダーSQLインジェクションの対策
信頼境界(Trust Boundary)
ブラウザ
従業員コード
氏名
SQL実行1
(INSERT)
安全なSQLクエリ
INSERT INTO employee
VALUES(:id, :name, …)
SQL DB
&#039;; DELETE FROM
employee --
SQL実行2
(SELECT)
氏名
&#039;; DELETE FROM employee --
氏名
SQL実行1
(UPDATE)
安全なSQLクエリ
UPDATE employee
SET name=:name …
氏名
SQL DB
35


# Page. 36

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

なんか、ややこしいですね
もっと簡単にできませんか?
36


# Page. 37

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

やりましょう!
37


# Page. 38

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

「値」には2種類ある
• どんな値でも安全に使える方法があるもの
– SQL中のリテラル（数値、文字列） プレースホルダ
– HTMLの要素内容、属性値 エスケープ
– …
• 「値」を信頼するしかないもの
– SQL文そのもの
– プログラムコード
– ログインユーザ名
– …
38


# Page. 39

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

再掲: そもそも信頼境界関係なくね?
信頼境界を越えて渡される信頼できないデータは無害化する
信頼境界
プレースホルダ
信頼できないデータ
安全な形でSQL
呼び出し
39


# Page. 40

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

どんな値でも安全に使える方法がある場合は淡々をそれをやる
•
•
•
•
脆弱性対処が局所化できる場合はそれがベスト
とりあえず、他に余計なことを考える必要はない
「脆弱性がないこと」がひと目で分かるのが理想
例
– SQLクエリに文字列連結を用いず、プレースホルダを用い
て行う
– HTML表示の際にもれなくエスケープ処理を行う
– ヘッダインジェクションが発生しないライブラリを用い
る
– OSコマンド呼び出しにシェル実行を伴わないAPIを用いる
※ PHPでは難しい
40


# Page. 41

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

「信頼できる値」が要求される場合は…
• いったん信頼境界の外に出た値は信頼で
きない
• 信頼が必要な値は信頼境界から外に出さ
ないで用いる
信頼できない値を
いくらバリデーションしても、
信頼できる値にはならない
41


# Page. 42

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

「信頼されたデータ」が要求される例
• 信頼境界の中のデータ
–
–
–
–
–
–
プログラムコード
SQL文
evalの入力
設定ファイル名に記載されたファイル名
正規表現
オブジェクト
42


# Page. 43

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

認証・認可により信頼を与えることは可能
• 認証・認可により「信頼できることを確認」
– ログイン済みユーザ名（認証）
– 管理者が入力するSQL文（認可）例: phpMyAdminで入力
するSQL文
– CMSに入力するHTML（認可） 例: WordPressで管理者
が入力するHTML
• フィルタリングにより「真正でないかもしれないが
無害」にする例
– 制限されたHTML（フィルタリング）
– 外部からのファイル名（basename）
43


# Page. 44

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

再掲: ディレクトリトラバーサルの対策(1)
信頼境界(Trust Boundary)
file=template.html
セッション変数
file=template.html
画面1
設定ファイル
file=
template.html
値を信頼境界の外に
出さないことで対策
fopen
ファイルシステム
fopen(&#039;template.html&#039;, …
44


# Page. 45

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

再掲: ディレクトリトラバーサルの対策(2)
信頼境界(Trust Boundary)
画面1
設定ファイル
file=
template.html
Hidden
パラメータ
basename
値を無害な形に
フィルタリング
fopen
ファイルシステム
file=passwd
fopen(&#039;passwd&#039;, …
45


# Page. 46

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

いったんまとめ
• 局所的に「絶対安全」な方法で実装すれば脆弱性は
混入しない
• 信頼境界の中だけを通ってきた値を用いる際には、
局所的な脆弱性はあっても、攻撃には至らない…が
確認を間違う場合がある
• 信頼できない値を安全に使う方法
– 認証・認可
– フィルタリング
– …これらの実装にバグがあると脆弱性になる
※ 局所的な安全を積み重ねることが、脆弱性を作り
込まない早道
46


# Page. 47

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

ケーススタディ
47


# Page. 48

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

Welcart 1.9.3 のオブジェクトインジェク
ション脆弱性
48


# Page. 49

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

Welcart 1.9.4 をリリースしました【脆弱性の修正】
Welcart 1.9.4 をリリースしました。オブジェクトインジェクション
脆弱性の修正などを行いました。詳細は以下の通りです。
アップグレードを行う場合は、Welcartを停止してからアップグレー
ドを行ってください。
【変更点】
• オブジェクトインジェクション脆弱性の修正
フロントにて、オブジェクトインジェクションと思われる脆弱性
が認められました。
過去のすべてのバージョンが対象となります。1.9.4にアップグ
レードしてください。
放置しますと、サイトに任意のファイルの埋め込まれる可能性が
あります。
脆弱性に関する修正の差分はこちら
https://www.welcart.com/community/archives/83947より引用
49


# Page. 50

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

Welcartフォーラムよりインシデント報告
https://www.welcart.com/community/forums/forum/バグ報告 より引用
50


# Page. 51

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

Welcart 1.9.3 と 1.9.4の差分
典型的な
オブジェクトインジェクション脆弱性
https://plugins.trac.wordpress.org/changeset?sfp_email=&amp;sfph_mail=&amp;reponame=&amp;new=1728429
40usc-e-shop&amp;old=1728428%40usc-e-shop&amp;sfp_email=&amp;sfph_mail= より引用
51


# Page. 52

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

オブジェクトインジェクションとは
• クッキー等からシリアライズデータを送り込み、任意のオブ
ジェクトをメモリ内に生成
• オブジェクトが破棄されるタイミングでデストラクタが実行
される
• オブジェクトを巧妙に組み合わせることにより、攻撃を実行
52


# Page. 53

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

オブジェクトインジェクションに学ぶ…
• unserialize関数に信頼できない値（信頼境界を超え
て来た値）を渡してはいけない
• やってはいけないことを知らずにやってしまったこ
とが根本原因
• 以下のものも同様
–
–
–
–
eval
system
call_user_func
call_user_func_array
• 外部入力を処理する場合は、JSONを用いましょう
53


# Page. 54

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

WordPress REST API のコンテンツインジェク
ション脆弱性


# Page. 55

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

WordPressの脆弱性突く攻撃が激増、6万以上のWebサイトで改ざん被害
脆弱性情報が公開されてから48時間足らずの間に悪用コードが投稿され、脆弱性のあるサイトを探して攻撃
を試す動きはインターネット全体に広がった。ハッキングされたWebサイトの数は6万6000以上にのぼり、
現在も増え続けている。
1月下旬のパッチで修正された、WordPressの深刻な脆弱性を突く攻撃が、わずか2週間足らずの間に激増
し、多数のWebサイトが改ざんなどの被害に遭っていることが分かった。この問題を発見したセキュリティ
企業のSucuriが2月6日のブログで伝えた。
WordPressは1月26日に公開した更新版の4.7.2で複数の脆弱性を修正した。このうち特に深刻なWordPress
REST APIの脆弱性については、2月1日まで待ってから情報を公開していた。この問題を悪用された場合、認
証を受けないユーザーがWordPressサイトのコンテンツやページを改ざんできてしまう可能性が指摘されて
いる。
脆弱性を悪用した攻撃のイメージ（出典：IPA）
Sucuriでは、脆弱性情報が公開されてから48時間足らずの間に悪用コードがWeb上に掲載され、共有され
ていることを確認した。その情報が簡単に入手できることから、脆弱性のあるサイトを探して攻撃を試す動
きはインターネット全体に広がったという。
http://www.itmedia.co.jp/enterprise/articles/1702/09/news064.html より引用
55


# Page. 56

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

権限チェックのupdate_item_permissions_checkメソッド
497:
498:
499:
500:
501:
502:
503:
504:
505:
506:
507:
508:
509:
510:
511:
512:
513:
public function update_item_permissions_check( $request ) {
$post = get_post( $request[&#039;id&#039;] );
$post_type = get_post_type_object( $this-&gt;post_type );
if ( $post &amp;&amp; ! $this-&gt;check_update_permission( $post ) ) {
return new WP_Error( ‘rest_cannot_edit’, __(Sorry, you are not allowed ...
}
if ( ! empty( $request[‘author’] ) &amp;&amp; get_current_user_id() !==
$request[&#039;author&#039;] &amp;&amp;
! current_user_can( $post_type-&gt;cap-&gt;edit_others_posts ) ) {
return new WP_Error( &#039;rest_cannot_edit_others&#039;, __( &#039;Sorry, you are not ...
}
if ( ! empty( $request[&#039;sticky&#039;] ) &amp;&amp;
! current_user_can( $post_type-&gt;cap-&gt;edit_others_posts ) ) {
return new WP_Error( &#039;rest_cannot_assign_sticky&#039;, __( &#039;Sorry, you are not ...
}
if ( ! $this-&gt;check_assign_terms_permission( $request ) ) {
return new WP_Error( &#039;rest_cannot_assign_term&#039;, __( &#039;Sorry, you are not ...
}
return true;
}
wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php (Ver 4.7.1)
56


# Page. 57

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

update_item_permissions_check() の返り値
コンテンツの性質
存在しないコンテンツ
存在し権限のあるコンテンツ
存在し権限のないコンテンツ
返り値
true
true
false
57


# Page. 58

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

update_item()メソッド
523:
524:
525:
526:
527:
528:
529:
530:
531:
532:
public function update_item( $request ) {
$id
= (int) $request[&#039;id&#039;];
$post = get_post( $id );
if ( empty( $id ) || empty( $post-&gt;ID )
|| $this-&gt;post_type !== $post-&gt;post_type ) {
return new WP_Error( ‘rest_post_invalid_id’,
__( &#039;Invalid post ID.&#039; ), array( &#039;status&#039; =&gt; 404 ) );
}
$post = $this-&gt;prepare_item_for_database( $request );
if ( is_wp_error( $post ) ) {
return $post;
}
58


# Page. 59

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

WordPress 4.7.1は何がいけなかったか?
• 原因:
– 権限チェックの際に、存在しない id に対して、権限あり
を返していた
– 権限チェックの際は id キャストなし、データ更新の際は
id を整数にキャストしていた
• 直接の対策
– 存在しない id に対しては「権限なし」を返す
– 権限チェックと更新の際には同じ id を用いる
• 原則論として
– 正規化（この場合は整数へのキャスト）は早期に実施す
る
– バリデーションしていれば防げましたね
59


# Page. 60

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

Joomla2.5.2の権限昇格脆弱性
60


# Page. 61

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

Joomla2.5.2の権限昇格脆弱性
攻撃の流れ
1. 会員登録時にパスワードを不整合にしておく
2. ユーザ登録時に jforms[groups][]=7 をPOSTパラ
メータに追加
3. バリデーションでエラー発生
4. 再入力に備えてリクエストのパラメータをすべて
セッションに保存（コントローラ）
5. モデル側で、セッションの中味をすべて取り込み
6. 2.で追加したgroupsが取り込まれる
61


# Page. 62

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

Joomla2.5.2の権限昇格脆弱性
components/com_users/controllers/registration.php register()関数
$data = $model-&gt;validate($form, $requestData);
// Check for validation errors.
バリデーションエラーの場合、リクエストデータを
まるごとセッション変数に放り込んでいる
if ($data === false) {
権限の情報も含まれている
// Save the data in the session.
$app-&gt;setUserState(&#039;com_users.registration.data&#039;, $requestData);
// Redirect back to the registration screen.
$this-&gt;setRedirect(JRoute::_(&#039;index.php?option=com_users&amp;view=registrat
ion&#039;, false));
return false;
// 中略
// バリデーションが正常の場合
// Flush the data from the session.
$app-&gt;setUserState(&#039;com_users.registration.data&#039;, null);
62


# Page. 63

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

components/com_users/models/registration.php getData() 関数内
$temp = (array)$app-&gt;getUserState(&#039;com_users.registration.data&#039;, array());
foreach ($temp as $k =&gt; $v) {
$this-&gt;data-&gt;$k = $v; // セッションのデータをモデルに放り込んでいる
}
【中略】
セッション汚染、Trust Boundary Violation
と呼ばれる問題
$this-&gt;data-&gt;groups = isset($this-&gt;data-&gt;groups) ? array_unique($this-&gt;da
ta-&gt;groups) : array();
// $this-&gt;data-&gt;groups = array(); 2.5.3でこのように修正
63


# Page. 64

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

Joomla2.5.2は何がいけなかったか?
• 原因:
– 入力フォームの値を、セッション変数経由で、モデルの
オブジェクトに丸ごと放り込んでいた
– セッション変数やオブジェクトに放り込む際にプロパ
ティの名前を確認していない(!)
• 対策
–
–
–
–
まずは、放り込みをやめる
せめてプロパティ名（キー名）を確認する
セッション変数等の濫用をやめる
結果として、信頼できる値とそうでない値を混ぜない
64


# Page. 65

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

phpMyAdminの正規表現イン
ジェクション
CVE-2013-3238
65


# Page. 66

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

phpMyAdmin: CVE-2013-3238
case &#039;replace_prefix_tbl&#039;:
$current = $selected[$i];
$newtablename = preg_replace(&quot;/^&quot; . $from_prefix . &quot;/&quot;,
$to_prefix, $current);
$from_pref = &quot;/e¥0“;
$to_prefix = &quot;phpinfo();”;
preg_replace(&quot;/^/e¥0/&quot;, &quot;phpinfo();&quot;, &quot;test&quot;);
PHP5.4.3以前では、¥0以降は無視される
preg_replace(&quot;/^/e&quot;, &quot;phpinfo();&quot;, &quot;test&quot;);
66


# Page. 67

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

脆弱性が混入した要因
• preg_replaceに渡す正規表現をエスケープしていな
かった
– 最低限、/ をエスケープする必要がある
preg_replace(“/^” . $from_prefix . “/”, …
↓
reg_replace(&quot;/^&quot; . preg_quote($from_prefix, &#039;/&#039;) . &quot;/&quot;, …
• えーっと、preg_quoteってマルチバイト対応?
– Shift_JIS以外では問題ない?
• そもそも、正規表現を外部から（信頼境界を超え
て）渡す実装は避けるべき（実際にもその方向で改
修された）
67


# Page. 68

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

正規表現インジェクションの対策の考え方
• PHPには、正規表現をエスケープする関数
preg_quote が用意されているが…
• そもそも、正規表現を外部から指定できる状況は、
極力避けるべき
• 【原則】外部由来の値は、正規表現として利用しな
い
• phpMyAdminの正規表現インジェクション脆弱性も、
正規表現を避ける形で実装された
68


# Page. 69

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

Drupageddon(CVE-2014-3704)
69


# Page. 70

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

Drupalのログイン処理のSQL文を調べる
通常時の要求
name=admin&amp;pass=xxxxxxxx&amp;form_build_id=form-xQZ7X78LULvs6SyB9Mvuf
bZh5KXjQYRHS05Jl2uD9Kc&amp;form_id=user_login_block&amp;op=Log+in
通常時のSQL文
SELECT * FROM users WHERE name = &#039;admin&#039; AND status = 1
nameを配列で指定
name[]=user1&amp;name[]=user2&amp;pass=xxxxxxxx&amp;form_build_id=form-xQZ7X7
8LULvs6SyB9MvufbZh5KXjQYRHS05Jl2uD9Kc&amp;form_id=user_login_block&amp;op
=Log+in
nameを配列にした場合のSQL文
SELECT * FROM users WHERE name = &#039;user1&#039;, &#039;user2&#039; AND status = 1
文字列リテラルが複数生成される
70


# Page. 71

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

IN句生成の便利な呼び出し方だが…
db_queryにてIN句のバインド値を配列にすると…
&lt;?php
db_query(&quot;SELECT * FROM {users} where name IN (:name)&quot;,
array(&#039;:name&#039;=&gt;array(&#039;user1&#039;,&#039;user2&#039;)));
?&gt;
IN句の値がプレースホルダのリストに展開される
SELECT * from users where name IN (:name_0, :name_1)
バインド値の配列は以下の様に変形される
array(&#039;:name_0&#039;=&gt;&#039;user1&#039;, &#039;:name_1&#039;=&gt;&#039;user2&#039;))
71


# Page. 72

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

キー名をつけると
キー名をつけてみる(id1, id2)
name[id1]=user1&amp;name[id2]=user2
プレースホルダにキー名がつく
SELECT * FROM {users} WHERE name = :name_id1, :name_id2 AND statu
s = 1
72


# Page. 73

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

空白付きのキー
キー名に空白をつけてみる
name[1 xxxxx]=user1&amp;name[2]=user2
プレースホルダに空白が含まれる
SELECT * FROM {users} WHERE name = :name_1 xxxxx, :name_2 AND sta
tus = 1
ちぎれたプレースホルダはSQL文
の一部として認識される
プレースホルダには、キー :name_1がないので上記のSQL文呼び出しはエラーになる
array(2) {
[&quot;:name_1 xxxxx&quot;] =&gt; &quot;user1&quot;
[&quot;:name_2&quot;] =&gt; &quot;user2&quot;
}
← :name_1 ではない
73


# Page. 74

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

バインド値のつじつまを合わせる
キー名に空白をつけてみる
name[2 xxxxx]=&amp;name[2]=user2
プレースホルダに空白が含まれる
SELECT * FROM {users} WHERE name = :name_2 xxxxx, :name_2 AND sta
tus = 1
プレースホルダ :name_2 が
2箇所現れる
プレースホルダ配列は上記SQL文の要求を満たすのでSQL文は呼び出される…
が、xxxxxの箇所でSQLの文法違反となる
array(2) {
[&quot;:name_2 xxxxx&quot;] =&gt; &quot;&quot;
[&quot;:name_2&quot;] =&gt; &quot;user2&quot;
}
74


# Page. 75

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

SQLインジェクションを試す
キー名に追加のSQL文を書く
name[2 ;SELECT sleep(10) -- ]=&amp;name[2]=user2
プレースホルダの後ろに追加のSQL文が現れる
SELECT * FROM {users} WHERE name = :name_2 ;SELECT sleep(10) -- ,
:name_2 AND status = 1
実際に呼び出されるSQL文
SELECT * FROM users WHERE name = &#039;user2&#039; ;SELECT sleep(10) -- , &#039;
user2&#039; AND status = 1
75


# Page. 76

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

脆弱なソース
// includes/database/database.inc
protected function expandArguments(&amp;$query, &amp;$args) {
$modified = FALSE;
// $argsの要素から配列のみ処理対象として foreach
foreach (array_filter($args, &#039;is_array&#039;) as $key =&gt; $data) {
$new_keys = array();
// $dataは配列であるはずなので、foreach 可能。 $i（キー）に注目
foreach ($data as $i =&gt; $value) {
$new_keys[$key . &#039;_&#039; . $i] = $value;
}
// $queryを改変 $new_keysのキーをarray_keysでSQL文に混ぜている
$query = preg_replace(&#039;#&#039; . $key . &#039;¥b#&#039;,
implode(&#039;, &#039;, array_keys($new_keys)), $query);
unset($args[$key]);
$args += $new_keys;
$modified = TRUE;
}
return $modified;
}
76


# Page. 77

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

対策版(7.32)
// includes/database/database.inc
protected function expandArguments(&amp;$query, &amp;$args) {
$modified = FALSE;
// $argsの要素から配列のみ処理対象として foreach
foreach (array_filter($args, &#039;is_array&#039;) as $key =&gt; $data) {
$new_keys = array();
// $dataは配列であるはずなので、foreach 可能。 $i（キー）に注目
//foreach ($data as $i =&gt; $value) {
foreach (array_values($data) as $i =&gt; $value) { // キーを削除
$new_keys[$key . &#039;_&#039; . $i] = $value;
}
// $queryを改変 $new_keysのキーをarray_keysでSQL文に混ぜている
$query = preg_replace(&#039;#&#039; . $key . &#039;¥b#&#039;,
implode(&#039;, &#039;, array_keys($new_keys)), $query);
unset($args[$key]);
$args += $new_keys;
$modified = TRUE;
}
return $modified;
}
77


# Page. 78

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

Drupageddonは何が問題だったか?
• Drupalは、DBアクセスにPDOのプレースホルダを
用いている
• プレースホルダを用いる側は問題ないはず…
• プレースホルダ入りのSQL文組み立てに問題があっ
た
– 配列の添字（整数を想定）経由で、SQL文に外部由来の値
が混入した
– 配列の添字に文字列が入るのは想定外だったわけだが…
• 対策版（最新版も同じ）では、array_values()関数に
より、配列の値だけを取り出すことに
• アドホックのように見えて、「局所安全」の考え方
には従っている
お前とは旨い酒が飲めそうだw
78


# Page. 79

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

安全なアプリケーションの作り方
79


# Page. 80

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

安全なウェブアプリケーションのための原則
• 局所的に脆弱性を解消する
– 局所的に安全な方法がとれるなら、「値の安全性」は気にしない
– プレースフォルダによるSQLインジェクション対策が代表例
• コード、命令に対して、外部からの値を持ち込まない
– プログラム(JavaScript含む)、SQL文、シェルコマンド、ファイル名、
正規表現
– HTMLとJSONは適当な方法がないのでエスケープで…
• 「ややこしいことが起きがちな」機能を避ける
– eval、system、call_user_func …
– 複雑な正規表現も避けた方が良い
– デストラクタ等に複雑な処理を書かない
• 防御的プログラミングを実践する
• 単体テストを徹底する
80


# Page. 81

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

PHP7 で堅牢なコードを書く - 例外処理、表明プログラミング、契約による設計
https://speakerdeck.com/twada/php-conference-2016 より引用
PHPカンファレンス2016
和田卓人さんの講演から
81


# Page. 82

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

PHPカンファレンス2016
和田卓人さんの講演から
PHP7 で堅牢なコードを書く
- 例外処理、表明プログラミング、契約による設計
PHP7 で堅牢なコードを書く
- 例外処理、表明プログラミング、契約による設計
https://speakerdeck.com/twada/php-conference-2016
より引用
https://speakerdeck.com/twada/php-conference-2016
より引用
82


# Page. 83

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

Drupageddonを例外処理と表明で対処しようとする
Drupal 7.32
foreach (array_values($data) as $i =&gt; $value) { // キーを削除
$new_keys[$key . &#039;_&#039; . $i] = $value;
}
例外処理
foreach ($data as $i =&gt; $value) {
if (! is_int($i)) {
throw new Exception(&#039;添字は整数である必要があります&#039;);
}
$new_keys[$key . &#039;_&#039; . $i] = $value;
}
表明
foreach ($data as $i =&gt; $value) {
assert(is_int($i));
$new_keys[$key . &#039;_&#039; . $i] = $value;
}
83


# Page. 84

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

「防御的」か「契約」か
• 表明に基づく「契約による設計」は、表明(assign)が実行時
に無効にされることが問題
• デバッグ時にassignに引っかかれば、問題に気づき修正でき
るが、脆弱性に対する攻撃では、それは期待できない
• 「発生が予想されるケース」を悲観的に（防御的に）判断し
て、実行時チェックを残しておく
• 実行時チェックを「しないですむ」方法の一つとして、
foreachをやめてfor文を使う方法もある
for ($i = 0; $i &lt; count($data); $i++) {
$new_keys[$key . &#039;_&#039; . $i] = $data[$i];
}
• バグが減れば（バグの一種である）脆弱性も減るので、表明
も適材適所で活用するとよい
84


# Page. 85

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

バリデーションはしましょうね
• バリデーション（フォームバリデーション）で脆弱
性対処ができるとは限らないが…
• アプリケーションの前提条件を確認するという意味
でバリデーションは重要
脆弱性
バリデーションで防げた
Welcart 1.9.3 のオブジェクトインジェクション
×
Joomla2.5.2の権限昇格脆弱性
×
WordPress REST API CVE-2017-1001000
phpMyAdminの正規表現インジェクション
○
Drupageddon(CVE-2014-3704)
○
（*1）
○
*1 Drupageddonは、ユーザ名が文字列型（配列でない）ことのチェック
を入れれば防げたが、この改修は Drupal 7.36で追加された
85


# Page. 86

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

まとめ
• 局所的に「絶対安全」な方法で実装すれば、脆弱性は混入し
ない
• 信頼境界の中だけを通ってきた値を用いる際には、局所的な
脆弱性はあっても、攻撃には至らない
…が確認を間違う場合がある
• 信頼できない値を安全に使う方法
– 認証・認可
– フィルタリング
– …これらの実装にバグがあると脆弱性になる
• 局所的な安全を積み重ねることが脆弱性を作り込まない早道
• 「信頼できるはずの値」でも、可能なら局所安全な方法で実
装することで、多重防御となる
• バリデーションは必ず行うが、あてにしないこと
86


