Recommended
PPTX
SPAセキュリティ入門~PHP Conference Japan 2021
PPTX
徳丸本に載っていないWebアプリケーションセキュリティ
PPTX
SSRF対策としてAmazonから発表されたIMDSv2の効果と破り方
PDF
【BS13】チーム開発がこんなにも快適に!コーディングもデバッグも GitHub 上で。 GitHub Codespaces で叶えられるシームレスな開発
PDF
PPTX
PDF
【BS3】Visual Studio 2022 と .NET 6 での Windows アプリ開発技術の紹介
PDF
PPTX
PDF
[AWS EXpert Online for JAWS-UG 18] 見せてやるよ、Step Functions の本気ってやつをな
PDF
Open Policy Agent (OPA) 入門
PPTX
PDF
PDF
金融ISAC アニュアルカンファレンス 2020:Intelligence Driven Securityの「ことはじめ」
PPTX
Reladomoを使ったトランザクション履歴管理をプロダクトに適用した際のメリット/デメリット/課題など
PPTX
PDF
PPTX
PPTX
PDF
[cb22] Hayabusa Threat Hunting and Fast Forensics in Windows environments fo...
PDF
使ってみて気づいた AGPL ライセンスの メリット・デメリット
PPTX
Selenium WebDriver + python で E2Eテスト自動化
PDF
PDF
機械学習を用いた仕様書からのテストケース自動生成ツールSpec2Testの試作
ODP
PDF
マイクロサービス化設計入門 - AWS Dev Day Tokyo 2017
PDF
PDF
PDF
徳丸本に学ぶ 安全なPHPアプリ開発の鉄則2011
PDF
More Related Content
PPTX
SPAセキュリティ入門~PHP Conference Japan 2021
PPTX
徳丸本に載っていないWebアプリケーションセキュリティ
PPTX
SSRF対策としてAmazonから発表されたIMDSv2の効果と破り方
PDF
【BS13】チーム開発がこんなにも快適に!コーディングもデバッグも GitHub 上で。 GitHub Codespaces で叶えられるシームレスな開発
PDF
PPTX
PDF
【BS3】Visual Studio 2022 と .NET 6 での Windows アプリ開発技術の紹介
PDF
What's hot
PPTX
PDF
[AWS EXpert Online for JAWS-UG 18] 見せてやるよ、Step Functions の本気ってやつをな
PDF
Open Policy Agent (OPA) 入門
PPTX
PDF
PDF
金融ISAC アニュアルカンファレンス 2020:Intelligence Driven Securityの「ことはじめ」
PPTX
Reladomoを使ったトランザクション履歴管理をプロダクトに適用した際のメリット/デメリット/課題など
PPTX
PDF
PPTX
PPTX
PDF
[cb22] Hayabusa Threat Hunting and Fast Forensics in Windows environments fo...
PDF
使ってみて気づいた AGPL ライセンスの メリット・デメリット
PPTX
Selenium WebDriver + python で E2Eテスト自動化
PDF
PDF
機械学習を用いた仕様書からのテストケース自動生成ツールSpec2Testの試作
ODP
PDF
マイクロサービス化設計入門 - AWS Dev Day Tokyo 2017
PDF
PDF
Similar to 安全なPHPアプリケーションの作り方2016
PDF
徳丸本に学ぶ 安全なPHPアプリ開発の鉄則2011
PDF
PDF
PDF
著名PHPアプリの脆弱性に学ぶセキュアコーディングの原則
PPTX
Webサイトをめぐるセキュリティ状況と効果的な防御方法(WordPress編)
PPTX
脆弱性は誰のせい? PHP、MySQL、Joomla! の責任やいかに
PPTX
PDF
今日こそわかる、安全なWebアプリの作り方2010
PPTX
PDF
徳丸本に学ぶ 安全なPHPアプリ開発の鉄則2012
PDF
PDF
PDF
PDF
「最近のwebアプリケーションの脆弱性やそれを悪用する攻撃の動向」OWASP Kansai
PDF
PPTX
デバッガでWordPress本体やプラグインの脆弱性を追いかけてみよう
PPT
PDF
PDF
PPTX
More from Hiroshi Tokumaru
PPTX
PPTX
XXE、SSRF、安全でないデシリアライゼーション入門
PPTX
文字コードの脆弱性はこの3年間でどの程度対策されたか?
PPTX
PPT
PPTX
ウェブ・セキュリティ基礎試験(徳丸基礎試験)の模擬試験問題
PPTX
PPTX
CMS四天王への攻撃デモを通じて、WordPressの効果的な防御法を学ぼう
PPTX
『例えば、PHPを避ける』以降PHPはどれだけ安全になったか
PPTX
Railsエンジニアのためのウェブセキュリティ入門
PPTX
PPTX
オニギリペイのセキュリティ事故に学ぶ安全なサービスの構築法 (PHPカンファレンス2019)
PPTX
脅威分析の手法によりウェブサーバーにウイルス対策ソフトが必要かを検証する
PPTX
PPTX
SecurityとValidationの奇妙な関係、あるいはDrupalはなぜValidationをしたがらないのか
PPTX
PDF
Rails SQL Injection Examplesの紹介
PPTX
introduction to unsafe deserialization part1
Recently uploaded
PDF
第21回 Gen AI 勉強会「NotebookLMで60ページ超の スライドを作成してみた」
PPTX
PDF
さくらインターネットの今 法林リージョン:さくらのAIとか GPUとかイベントとか 〜2026年もバク進します!〜
PDF
PDF
2025→2026宙畑ゆく年くる年レポート_100社を超える企業アンケート総まとめ!!_企業まとめ_1229_3版
PDF
100年後の知財業界-生成AIスライドアドリブプレゼン イーパテントYouTube配信
PDF
Starlink Direct-to-Cell (D2C) 技術の概要と将来の展望
PDF
Reiwa 7 IT Strategist Afternoon I Question-1 Ansoff's Growth Vector
PDF
Reiwa 7 IT Strategist Afternoon I Question-1 3C Analysis
安全なPHPアプリケーションの作り方2016 1. 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月)
「徳丸浩のWebセキュリティ教室 」(2015年10月)
– 技術士(情報工学部門)
Copyright © 2016 HASH Consulting Corp. 2
3. 4. 5. CVE-2016-8869等はどのような問題か?
• どのような問題か?
– ユーザー登録時に、管理者権限を設定されてしまう(CVE-2016-8869)
– ユーザー登録を許可していない設定でもユーザー登録ができてしまう(CVE-2016-8870)
• なにが原因だったか?
– Joomla!内にユーザー登録のメソッドが2つ存在した
• UsersControllerRegistration::register()
• UsersControllerUser::register()
– UsersControllerUser::register() の方は、ユーザー登録許可の設定を確認していない!
– 同じく、外部から権限を示すコードを設定可能
– UsersControllerUser::register()を外部から呼び出す経路が存在した
• どう対策したか?
– UsersControllerUser::register()の削除
• 利用者はどうすればよいか?
– Joomla!のバージョンアップ
Copyright © 2016 HASH Consulting Corp. 5
Demo
6. CVE-2016-8869等から得られる教訓
• 使わなくなったコードは速やかに削除しよう
• 脆弱性診断の古典的な観点ではあるが…
– ソースを追わないと判らない場合が多い
– 内部構造を熟知していないと、短期間の脆弱性診断では指摘できない
• 脆弱性診断で、「使わないコードを削除する」ように勧めている理由
– 古いコードには脆弱性があるかもしれない
– 拡張を.bak等に変更しているとソースコードが閲覧できてしなう
– デバッグ機能等の場合は、バックドアとして悪用される可能性
• 「古いコード」がこれほどまでに「悪用可能」な例は珍しく、ちょっ
と興奮した
• やはり、古いコードは速やかに削除しよう
Copyright © 2016 HASH Consulting Corp. 6
← 違法ではないが一部不適切
7. Joomla!には類似脆弱性の”前科”がある
• Joomla! 2.5.2以前に存在した脆弱性
• ユーザー登録時に、権限情報を付与し、
かつバリデーションエラーを発生させる
ことで権限昇格が可能。
• NICT(情報通信研究機構)の研究者のサ
イトが改ざんされる事件に悪用された
Copyright © 2016 HASH Consulting Corp. 7
https://developer.joomla.org/security-centre/395-
20120303-core-privilege-escalation.html より引用
8. 9. Joomla2.5.2の権限昇格脆弱性
components/com_users/controllers/registration.php register()関数
$data = $model->validate($form, $requestData);
// Check for validation errors.
if ($data === false) {
// Save the data in the session.
$app->setUserState('com_users.registration.data', $requestData);
// Redirect back to the registration screen.
$this->setRedirect(JRoute::_('index.php?option=com_users&view=registration', false));
return false;
// 中略
// バリデーションが正常の場合
// Flush the data from the session.
$app->setUserState('com_users.registration.data', null);
Copyright © 2016 HASH Consulting Corp. 9
バリデーションエラーの場合、リクエストデータを
まるごとセッション変数に放り込んでいる
権限の情報も含まれている
10. components/com_users/models/registration.php getData() 関数内
$temp = (array)$app->getUserState('com_users.registration.data', array());
foreach ($temp as $k => $v) {
$this->data->$k = $v; // セッションのデータをモデルに放り込んでいる
}
【中略】
$this->data->groups = isset($this->data->groups) ?
array_unique($this->data->groups) : array();
// $this->data->groups = array(); 2.5.3でこのように修正
Copyright © 2016 HASH Consulting Corp. 10
セッション汚染、Trust Boundary Violation
と呼ばれる問題
11. セッション汚染脆弱性とは?
• セッション変数を外部から変更できる脆弱性
• 例: phpMyAdmin CVE-2011-2505
Copyright © 2016 HASH Consulting Corp. 11
if (strstr($_SERVER['QUERY_STRING'],'session_to_unset') != false)
{
parse_str($_SERVER['QUERY_STRING']);
session_write_close();
session_id($session_to_unset);
session_start();
$_SESSION = array();
session_write_close();
session_destroy();
exit;
}
parse_str関数に外部入力を与え、
第二引数なしで使うと、
register_globals以上に危険。
セッション変数の書き換えが可能
libraries/auth/swekey/swekey.auth.lib.php 266行目以降
12. セッション汚染でセッション変数にオブジェクトを突っ込む
• これができると危険だが…
– 脆弱性(PHPあるいはアプリケーション)がないとできない
• Joomla!みたいにRequestをごそっとセッション変数に代入したり、
parse_str関数を使っていても、オブジェクトは挿入できない
• 以下のようなケース
– PHPの脆弱性を使う
• CVE-2015-6835 (Joomla! に対するゼロデイ攻撃で悪用された)
• CVE-2016-7125 (後述)
– 入力値をunserialize関数を通している
• phpMyAdminの CVE-2011-2505 (前述)
– その他、eval等(オブジェクトインジェクション以前に危険だが…)
• オブジェクトインジェクション以前の問題として危険
Copyright © 2016 HASH Consulting Corp. 12
13. 14. 脆弱なサンプル(続き)
<?php // Logger.php
class Logger {
const LOGDIR = '/tmp/'; // ログ出力ディレクトリ
private $filename = ''; // ログファイル名
private $log = ''; // ログバッファ
public function __construct($filename) { // ファイル名を指定
if (! preg_match('/A[a-z0-9.]+z/i', $filename)) {
throw new Exception(‘Logger: ファイル名は英数字とドットで…');
}
$this->filename = $filename; // ファイル名
$this->log = ''; // ログバッファ
}
public function add($log) { // ログ出力
$this->log .= $log; // バッファに追加するだけ
}
}
Copyright © 2016 HASH Consulting Corp. 14
15. 脆弱なサンプル(続き)
public function __destruct() { // デストラクタではバッファの中身をファイルに書き出し
$path = self::LOGDIR . $this->filename; // ファイル名組み立て(局所的な脆弱性)
$fp = fopen($path, 'a');
if ($fp === false) {
die('Logger: ファイルがオープンできません' . htmlspecialchars($path));
}
if (! flock($fp, LOCK_EX)) { // 排他ロックする
die('Logger: ファイルのロックに失敗しました');
}
fwrite($fp, $this->log); // ログの書き出し
fflush($fp); // フラッシュしてからロック解除
flock($fp, LOCK_UN);
fclose($fp);
}
}
Copyright © 2016 HASH Consulting Corp. 15
16. 17. 先のサンプルはオブジェクト・インジェクション以前の問題
• 本質的な問題はSession Poisoning
• 以下のような攻撃
• http://example.jp/?userid=yamada&login=1
– $_SESSION[‘userid’] === ‘yamada’
– $_SESSION[‘login’] === ‘1’ (TRUEと評価され得る)
– → セッションハイジャックができてしまう
• すなわち、オブジェクト・インジェクション以前の問題として、セッ
ション汚染単体で問題となる可能性が高い
Copyright © 2016 HASH Consulting Corp. 17
18. CVE-2016-7125の対策
• PHPを最新版にする
– RHEL / CentOSではパッチが出ていないことに注意
– Debian / Ubuntu / Fedora のサポート中のバージョンは問題ない
• そもそもセッション汚染の状態を避ける
Copyright © 2016 HASH Consulting Corp. 18
https://access.redhat.com/security/cve/cve-2016-7125 より引用
19. 20. 21. 22. ケータイキット for Movable Type の脆弱性 (CVE-2016-1204) に関する注意喚起
各位
JPCERT-AT-2016-0019
JPCERT/CC
2016-04-26(新規)
2016-05-06(更新)
<<< JPCERT/CC Alert 2016-04-26 >>>
ケータイキット for Movable Type の脆弱性 (CVE-2016-1204) に関する注意喚起
https://www.jpcert.or.jp/at/2016/at160019.html
I. 概要
アイデアマンズ株式会社のケータイキット for Movable Type には、OS コマンドインジェクションの脆弱性 (CVE-2016-1204) があります。この
脆弱性を悪用された場合、当該製品が動作するサーバ上で任意の OS コマンドを実行される可能性があります。
本脆弱性や影響の詳細については、以下を参照してください。
Japan Vulnerability Notes JVNVU#92116866
ケータイキット for Movable Type に OS コマンドインジェクションの脆弱性
https://jvn.jp/vu/JVNVU92116866/
なお、本脆弱性を悪用した攻撃活動が確認されているとの情報があります。
https://www.jpcert.or.jp/at/2016/at160019.html より引用 22
23. 24. 脆弱なソースコード(Ver 1.641)
$wallpaper = isset($_GET['mtkk_wallpaper'])? unserialize($_GET['mtkk_wallpaper']): null;
// 中略
$cl = (isset($wallpaper['left']) && $wallpaper['left'])? $wallpaper['left']: 0;
// 中略
$options['left'] = $cl;
// 中略
if($options['left'] != 0 || $options['top'] != 0 || $options['width'] != $id['w']
|| $options['height'] != $id['h']) {
$dest_tmp = "$dest-crop.bmp";
$option = " -crop{$options['width']}x{$options['height']}+{$options['left']}+{$options['t
op']}";
$execute = "$convert $option $src $dest_tmp";
if($this->debug_mode) {
// 中略
} else {
exec($execute);
}
24
25. 対策版(Ver 1.65)
$wallpaper = isset($_GET['mtkk_wallpaper'])? unserialize($_GET['mtkk_wallpaper']): null;
// 中略
$cl = (isset($wallpaper['left']) && $wallpaper['left'])? $wallpaper['left']: 0;
// 中略
$options['left'] = $cl;
// 中略
if($options['left'] != 0 || $options['top'] != 0 || $options['width'] != $id['w']
|| $options['height'] != $id['h']) {
$dest_tmp = "$dest-crop.bmp";
$option = " -crop " . escapeshellarg("{$options['width']}x{$options['height']}+{$options
['left']}+{$options['top']}");
$execute = "$convert $option " . escapeshellarg($src) . " " . escapeshellarg($dest_tmp);
if($this->debug_mode) {
// 中略
} else {
exec($execute);
}
25
26. 27. 28. 29. もっと良い方法があった!
• ポイントはこれだけです
1. シェルスクリプトは静的な文字列として定義する。
2. パラメーターは環境変数で渡す。
3. エスケープ不要!
• 要は SQL のプリペアードステートメントみたいなものです。
29
#!/usr/bin/ruby
def getent_egrep_sed(db, pattern, script)
env = {
'db' => db,
'pattern' => pattern,
'script' => script,
}
system(env, 'getent -- "$db" |egrep -- "$pattern" |sed -- "$script"')
end
getent_egrep_sed(*ARGV[0..2])
https://fumiyas.github.io/2013/12/21/dont-use-shell.sh-advent-calendar.html より引用
30. PHPによるサンプルコード
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("file", "/tmp/error-output.txt", "a") // stderr .. temporary file
);
$arg2 = "; cat /etc/passwd #ntest test test";
$env = array('e_arg1' => 'aeiou; "xxx"', 'e_arg2' => $arg2);
$process = proc_open('./argdump "$e_arg1" "$e_arg2"', $descriptorspec, $pipes, getcwd(), $env);
if (is_resource($process)) {
fclose($pipes[0]);
while(!feof($pipes[1]))
echo fread($pipes[1], 10);
fclose($pipes[1]);
$return_value = proc_close($process);
echo "command returned $return_valuen";
}
Copyright © 2016 HASH Consulting Corp. 30
31. ふみやす方式による"安全なsystem関数"
function e_system($cmd, ...$args) {
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "/tmp/error-output.txt", "a"));
$cmdline = $cmd;
$env = [];
foreach ($args as $key => $value) {
$argname = 'e_arg_' . $key; // これは本当はよろしくない。局所的な脆弱性
$cmdline .= ' "$' . $argname . '"';
$env[$argname] = $value;
}
$process = proc_open($cmdline, $descriptorspec, $pipes, getcwd(), $env);
if (is_resource($process)) {
fclose($pipes[0]);
while(!feof($pipes[1]))
echo fread($pipes[1], 10);
fclose($pipes[1]);
return proc_close($process);
}
return FALSE;
}
e_system('/usr/sbin/sendmail', '[email protected] ; cat /etc/passwd'); // パラメータは別個の引数として指定
Copyright © 2016 HASH Consulting Corp. 31
32. 改善方針1
function e_system($cmd, ...$args) {
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "/tmp/error-output.txt", "a"));
$cmdline = $cmd;
$env = [];
for ($i = 0; $i < count($args); $i++) { // わかりやすく for文にしてみました
$argname = 'e_arg_' . $i; // これは本当はよろしくない。局所的な脆弱性
$cmdline .= ' "$' . $argame . '"';
$env[$argname] = $args[$i];
}
$process = proc_open($cmdline, $descriptorspec, $pipes, getcwd(), $env);
if (is_resource($process)) {
fclose($pipes[0]);
while(!feof($pipes[1]))
echo fread($pipes[1], 10);
fclose($pipes[1]);
return proc_close($process);
}
return FALSE;
}
e_system('/usr/sbin/sendmail', '[email protected] ; cat /etc/passwd'); // パラメータは別個の引数として指定
Copyright © 2016 HASH Consulting Corp. 32
33. 改善方針2 (for文を使うと死んでしまう人向け)
function e_system($cmd, ...$args) {
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "/tmp/error-output.txt", "a"));
$cmdline = $cmd;
$env = [];
foreach (array_values($args) as $key => $value) { // array_values関数でキー文字列を削除
$argname = 'e_arg_' . $key; // これは本当はよろしくない。局所的な脆弱性
$cmdline .= ' "$' . $argname . '"';
$env[$argname] = $value;
}
$process = proc_open($cmdline, $descriptorspec, $pipes, getcwd(), $env);
if (is_resource($process)) {
fclose($pipes[0]);
while(!feof($pipes[1]))
echo fread($pipes[1], 10);
fclose($pipes[1]);
return proc_close($process);
}
return FALSE;
}
e_system('/usr/sbin/sendmail', '[email protected] ; cat /etc/passwd'); // パラメータは別個の引数として指定
Copyright © 2016 HASH Consulting Corp. 33
34. 35. phpMyAdmin: CVE-2013-3238
Copyright © 2016 HASH Consulting Corp. 35
case 'replace_prefix_tbl':
$current = $selected[$i];
$newtablename = preg_replace("/^" . $from_prefix . "/", $to_prefix, $current);
preg_replace("/^/e0/", "phpinfo();", "test");
preg_replace("/^/e", "phpinfo();", "test");
$from_pref = "/e0“;
$to_prefix = "phpinfo();”;
PHP5.4.3以前では、0以降は無視される
36. 脆弱性が混入した要因
• preg_replaceに渡す正規表現をエスケープしていなかった
– 最低限、/ をエスケープする必要がある
• えーっと、preg_quoteって、マルチバイト対応だっけ?
– Shift_JIS以外では問題ない?
• そもそも、正規表現を外部から(信頼境界を超えて)渡す実装は
避けるべき(実際にもその方向で改修された)
Copyright © 2016 HASH Consulting Corp. 36
preg_replace(“/^” . $from_prefix . “/”, …
↓
reg_replace("/^" . preg_quote($from_prefix, '/') . "/", …
37. phpMyAdmin 4.6.2 librariestransformations.lib.php
function PMA_Transformation_globalHtmlReplace($buffer, $options = array())
{
if (! isset($options['string'])) {
$options['string'] = '';
}
if (isset($options['regex']) && isset($options['regex_replace'])) {
$buffer = preg_replace(
'@' . str_replace('@', '@', $options['regex']) . '@si',
$options['regex_replace'],
$buffer
);
}
// Replace occurrences of [__BUFFER__] with actual text
$return = str_replace("[__BUFFER__]", $buffer, $options['string']);
return $return;
}
Copyright © 2016 HASH Consulting Corp. 37
正規表現中の @ を @ にエ
スケープしているが不完全
38. 39. 40. 41. Zend Frameworkとは?
• PHPの心臓部であるZend Engineを開発しているZend Technologies社
が開発したアプリケーションフレームワーク
• 柔軟な構造であり自由な使い方ができる
• 依存関係が弱くコンポーネントとして利用が容易
• PHPのオブジェクト指向を活用している
• …
• 要はZend謹製のフレームワーク
Copyright © 2008-2015 HASH Consulting Corp. 41
42. 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. 42
43. 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. 43
44. ここで一つ疑問ががが
• 識別子はクォートとエスケープがされる
– 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. 44
// ( と ) があれば
// 式とみなす
45. 46. 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. 46
47. 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. 47
48. 49. 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. 49
50. 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. 50
51. 52. これはフレームワークの脆弱性なのか?
• Ruby on Railsの場合、orderメソッドに指定する文字列は「式」と決
まっているので、アプリケーション側のバリデーション等で対策する
ことが求められる
• Zend Frameworkの場合は、文字列の内容により識別子か式かが決ま
るので、責任境界があいまい
• 本来、識別子用のメソッドと式用のメソッドは、名前などで明確に区
別するべし
• つまり、Zend Frameworkの仕様の問題である
Copyright © 2008-2015 HASH Consulting Corp. 52
53. 54. 55. Zend Framework 1.12.10 での修正
• Zend Framework 1.12.10で関数呼び出し(括弧)のネストを許容した
• 式の判定が以下のように修正された
// 1.12.10
if (preg_match('/^([w]*(([^)]|(?1))*))$/', (string) $val)) {
$val = new Zend_Db_Expr($val);
}
Copyright © 2008-2015 HASH Consulting Corp. 55
https://regexper.com/ を利用
56. Zend Framework 1.12.10 での修正(続き)
• この修正により、以下のようなORDER BY句が許容された
– ORDER BY ((list_price - discount_price) * quantity)
• だが、穴はないのか?
• Zend Framework 1.12.10で混入した脆弱性
– MD5("(");DELETE FROM p2; #)
Copyright © 2008-2015 HASH Consulting Corp. 56
https://regexper.com/ を利用
57. 58. Zend Framework 1.12.19 での修正
• 式の判定が以下のように修正された
// 1.12.19
if (preg_match'/^([w]+s*(([^()]|(?1))*))$/', (string) $val)) {
$val = new Zend_Db_Expr($val);
}
Copyright © 2008-2015 HASH Consulting Corp. 58
禁止文字に開き括
弧が追加された
https://regexper.com/ を利用
59. 60. Zend Framework 1.12.19への攻撃
• 穴はないのか?
• Zend Framework 1.12.19に対する攻撃
– MD5(“a(");DELETE FROM p2; #)
Copyright © 2008-2015 HASH Consulting Corp. 60
https://regexper.com/ を利用
61. Zend Framework 1.12.20 での修正
• ORDER BY句のチェックに先立ち、SQLコメントを取り除く処理が追
加された
Copyright © 2008-2015 HASH Consulting Corp. 61
const REGEX_SQL_COMMENTS = '@
((['"]).*?[^]2) # $1 : Skip single & double quoted expressions
|( # $3 : Match comments
(?:#|--).*?$ # - Single line comments
| # - Multi line (nested) comments
/* # . comment open marker
(?: [^/*] # . non comment-marker characters
|/(?!*) # . ! not a comment open
|*(?!/) # . ! not a comment close
|(?R) # . recursive case
)* # . repeat eventually
*/ # . comment close marker
)s* # Trim after comments
|(?<=;)s+ # Trim after semi-colon
@msx';
62. 63. Zend Framework 1.12.20 への攻撃はできないのか?
• 実はまだ穴は残っている (;´Д`)
• Zend Framework 1 は2016年9月28日でEnd of Life(EOL)となった
https://framework.zend.com/blog/2016-06-28-zf1-eol.html
• すなわち、もう改修の見込みはない
• ダメ元で報告したけど、「アプリケーション側で対応せよ」という回答だった
• 実は僕もそう思う
Copyright © 2008-2015 HASH Consulting Corp. 63
64. 対策
• Zend Framework 1 をお使いの方は…
– 最新のZend Framework 1.12.20 に移行する かつ
– orderメソッドの引数をバリデーション
• できるだけ早期にZend Framework 2 または 3、あるいはその他のフ
レームワークに移行しましょう
※ Zend Framework 2 にはこの問題はありません
Copyright © 2008-2015 HASH Consulting Corp. 64
65. 66. JavaScriptの文字列リテラルのエスケープは難しい
• イベントハンドラ onxxxx= のエスケープは、
– まず文字列をJavaScript文字列としてエスケープして、
– HTMLの属性値としてエスケープする
• script要素の中身のエスケープは、
– JavaScript文字列としてエスケープするだけでよいが、
– </script>を注入されることによる攻撃の対策が必要となる
– 以下のようなケース
<script>
foo("☆ここを動的生成☆");
</script>
Copyright © 2016 HASH Consulting Corp. 66
67. 68. script要素内の文字列リテラル動的生成アプローチ
• 過剰エスケープ
– 徳丸本が紹介している方法の一つ
– 英数字以外を全てユニコードエスケープ uXXXX とエスケープする
– 実はあまり好きではない
• HTMLノードとして文字列を生成して、JavaScriptから参照する
– 古典的にはhiddenフィールド(徳丸本で紹介している方法の一つ)
– 最近だと id="hoge" data-foo="<% bar %>" しておいて $("#hoge").data('foo')
でとりだすのが主流かと思います。
http://b.hatena.ne.jp/entry/blog.ohgaki.net/javascript-string-escape
• 奥一穂氏の提唱する方法(Inline JSONP)
– ひとことでいうと、JSONPと同形式の呼出をサーバサイドで生成しSCRIPTタグ
として埋め込む、という手法を採るべきだと思います。
http://d.hatena.ne.jp/kazuhooku/20131106/1383690938
Copyright © 2016 HASH Consulting Corp. 68
69. 70. 71. 72. 73. わかりやすく書こう-うますぎるプログラムはいけない*1
• こなれた機能を使ってシンプルに実装すること
• 「危険な機能」は原則として避ける
– eval、system、call_user_func …
– 複雑な正規表現も避けた方が良い
Copyright © 2016 HASH Consulting Corp. 73
const REGEX_SQL_COMMENTS = '@
((['"]).*?[^]2) # $1 : Skip single & double quoted expressions
|( # $3 : Match comments
(?:#|--).*?$ # - Single line comments
| # - Multi line (nested) comments
/* # . comment open marker
(?: [^/*] # . non comment-marker characters
|/(?!*) # . ! not a comment open
|*(?!/) # . ! not a comment close
|(?R) # . recursive case
)* # . repeat eventually
*/ # . comment close marker
)s* # Trim after comments
|(?<=;)s+ # Trim after semi-colon
@msx'; Zend Framework 1.12.20 に出て来る正規表現
*1 プログラム書法、 B.カーニハン著、木村泉訳より引用
74. 75.