4.6K Views
April 16, 16
スライド概要
PHPカンファレンス北海道2016基調講演
『例えば、PHPを避ける』以降PHPはどれだけ 安全になったか 徳丸 浩
アジェンダ • • • • • • • • • • 例えば、PHPを避ける htmlspecialchars 文字エンコーディングチェックの改善 register_globalsが非推奨に マジッククォートが非推奨に 暗号学的に安全な擬似乱数生成器のサポート セッションID生成の安全性強化 ヌルバイト攻撃の防御機能の追加 PDOのDB接続時の文字エンコーディング指定が可能に header関数のバグ修正 安全なパスワード保存が簡単にできるようになった Copyright © 2016 Hiroshi Tokumaru 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 Hiroshi Tokumaru 3
例えば、PHPを避ける Copyright © 2016 Hiroshi Tokumaru 4
例えば、PHPを避ける Copyright © 2016 Hiroshi Tokumaru 5
現在のセキュアプログラミング講座 register globalsとナルバイト攻撃が問題??? https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/003. html より引用 6
PHPの何が問題だったか、本当に問題だったか? • セキュアプログラミング講座の改訂は2007年6月 • その時点で、PHPは本当に避けるべき存在だった か? • 当時の最新版はPHP 5.2.3 (2007/5/31) • その後、PHPはどの程度の安全になったか? • 下記について調査 – PHPの安全でない機能の削除 – PHPの安全性を高める機能の追加 Copyright © 2016 Hiroshi Tokumaru 7
htmlspecialchars 文字エンコーディング チェックの改善(PHP5.2.5 2007/11/8) Copyright © 2016 Hiroshi Tokumaru 8
htmlspecialcharsの文字コードチェックの変遷 • PHP4.1.0 (2001/12/10) htmlspecialcharsに第3引数追加。ほとんど何もして いないに等しい文字エンコーディングチェック • PHP-5.2.5 (2007/11/8) 文字エンコーディングのチェックを強化…したけど 抜けがたくさん • PHP-5.2.12 (2009/12/17) moriyoshiの神対応による厳格なチェックに • PHP-5.4.0 (2012/3/1) 第3引数のデフォルトが UTF-8 に変更 Copyright © 2016 Hiroshi Tokumaru 9
そこそこ安全なはずのスクリプト
<?php
header('Content-Type: text/html; charset=Shift_JIS');
$p1 = @$_GET['p1'];
$p2 = @$_GET['p2'];
?><body><form>
<input name=p1 value="<?php echo
htmlspecialchars($p1, ENT_QUOTES, 'Shift_JIS'); ?>"><BR>
<input name=p2 value="<?php echo
htmlspecialchars($p2, ENT_QUOTES, 'Shift_JIS'); ?>"><BR>
<input type="submit" value="更新">
</form></body>
Copyright © 2016 Hiroshi Tokumaru
10
半端な先行バイトによるXSS 閉じる引用符が食われた状態 <input name=p1 value="・><BR> <input name=p2 value=" onmouseover=alert(document.cookie)//"><BR> ここで最初の属性値がようやく終了 第二の属性値がイベントハンドラに • 半端な先行バイトによるXSSが発生する条件は、 以下のいずれかを満たす場合 – htmlspecialcharsの第3引数を指定していない – PHPの5.2.11以前あるいはPHP5.3.1以前を使用 • 対策としては、以下の両方を行う – PHPの最新版を使う – htmlspecialcharsの第3引数を指定する Copyright © 2010-2014 HASH Consulting Corp. 11
register_globalsが非推奨に (PHP-5.3.0 2009/6/30) Copyright © 2016 Hiroshi Tokumaru 12
register_globals=On の危険な例 session_start(); if (isset($_SESSION['user'])) { $islogin = TRUE; } Copyright © 2016 Hiroshi Tokumaru 13
履歴 • PHP-4.2.0 (2002/4/22) register_globalsがデフォルトで off になる • PHP-5.3.0 (2009/6/30) register_globalsを有効にすると警告エラーになる • PHP-5.4.0 (2012/3/1) register_globalsが廃止される Copyright © 2016 Hiroshi Tokumaru 14
マジッククォートが非推奨に (PHP-5.3.0 2009/6/30) Copyright © 2016 Hiroshi Tokumaru 15
マジッククォートとは何か? • 入力値($_GET、$_POST、$_COOKIE)を予めエスケー プしておく設定 – ' → \' \ → \\ • SQLインジェクション対策の自動化のために導入さ れた • PHP-5.3.0 (2009/6/30) マジッククォートを有効にすると警告エラーになる • PHP-5.4.0 (2012/3/1) マジッククォートが廃止される Copyright © 2016 Hiroshi Tokumaru 16
マジッククォートはなぜダメだったか? • 不便 – システムが勝手にエスケープするので多重エスケープの原因になる – エスケープが不要な場合アンエスケープの必要があり、不便であり、 脆弱性の要因にもなる • 対策として不十分(文字エンコーディングを考慮しないため …PDOの項参照) • 徳丸の意見 – マジッククォートは入力時にエスケープ処理を自動的行う仕組みだ が、エスケープ処理は文字列を使う時に都度すべきという考え方が 一般化した – マジッククォートはMySQLに特化したエスケープ方式であり、かつ MySQLのオプションや文字エンコーディングを考慮しない不完全な エスケープだった Copyright © 2016 Hiroshi Tokumaru 17
デモメモ: ここでphp.iniを編集して、 register_globalsとmagic_quotes_gpcを オフにしておく Copyright © 2016 Hiroshi Tokumaru 18
暗号学的に安全な擬似乱数生成器の サポート(PHP-5.3.0 2009/6/30) Copyright © 2016 Hiroshi Tokumaru 19
PHPにおける乱数の状況 • PHPにおける乱数の状況は酷い…下記が用いられる – rand() – mt_rand() – uniqid() • 上記はいずれも暗号学的に安全でない – 過去の乱数列から推測可能性があるということ • PHP-5.3.0から下記がサポートされる – openssl_random_pseudo_bytes() • PHP-7.0.0から下記がサポートされる – random_bytes() – random_int() Copyright © 2016 Hiroshi Tokumaru 20
セッションID生成の安全性強化 (PHP-5.3.2 2010/3/4) Copyright © 2016 Hiroshi Tokumaru 21
PHPはデフォルト設定では以下の組み合わせにMD5ハッシュ関数を通す方法でセッ ションIDを生成しています。 ➢ リモートIP アドレス ➢ 現在時刻 ➢ 乱数(暗号論的擬似乱数生成系ではない) これは、図4-51で示したありがちなセッションIDの生成方法に該当します。ロジックの 複雑性が高いため解読方法が判明しているわけではありませんが、理論的には安全性 が保証されていない設計ということになります。 体系的に学ぶ 安全なWebアプリケーションの作り方 P162、163より引用 22
http://dsas.blog.klab.org/archives/52136166.html より引用 23
セッションIDの強化の歴史 • PHP-5.3.2 (2010/3/4) セッションIDの生成方法を複雑化したが不完全 • PHP-5.4.0 (2012/3/1) セッションIDのシードに安全な乱数を使うように • PHP-5.4未満の場合は下記を設定するとよい [Session] ;; entropy_file は Windowsでは設定不要 ;; PHP-5.4以降では下記がデフォルトに session.entropy_file = /dev/urandom session.entropy_length = 32 Copyright © 2016 Hiroshi Tokumaru 24
ヌルバイト攻撃の防御機能の追加 (PHP-5.3.4 2010/12/9) Copyright © 2016 Hiroshi Tokumaru 25
ヌルバイト攻撃はディレクトリトラバーサル等と併用する https://www.ipa.go.jp/security/vuln/websecurity.html より引用 26
ディレクトリトラバーサルとヌルバイト攻撃 • 以下のPHPスクリプト $fp = fopen('./data/' . $_GET['file'] . '.txt', 'r'); … • file=../../../../../etc/passwd%00 とすると ファイル名は以下となる。[nul]は値0の文字 ./data/../../../../../etc/passwd[nul].txt カレントディレクトリが /var/www/html とすると /var/www/html/../../../../../etc/passwd[nul].txt ↓ 正規化 ([nul]以降は無視される) /etc/passwd • Unix / Linux / WindowsのAPIでは通常ヌルバイトを 文字列の終端記号として用いているため Copyright © 2016 HASH Consulting Corp. 27
ディレクトリトラバーサルの影響と対策 • 影響 – 任意のファイルの読み出し – 任意のファイルに任意内容が書き込みできる場合も – PHPスクリプト等を書き込みできれば、任意スクリプト を外部から自由に実行できる場合も • 対策 – ファイル名には basename()関数を通してから使う $file = basename($_GET['file']); – できるだけ新しいPHPを使う • PHP5.3.4以降ではヌルバイト攻撃対策がされている Copyright © 2016 HASH Consulting Corp. 28
PHP 5.3.4におけるヌルバイト攻撃対策 • ファイル名等にヌルバイトが混入している場合、エ ラーとして処理を打ち切る $ cat nullbyte.php <?php $rtn = readfile("../../../../etc/passwd\0.txt"); var_dump($rtn); $ php-5.3.3 nullbyte.php root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh 【中略】 int(1415) $ php-5.3.4 nullbyte.php bool(false) Copyright © 2016 HASH Consulting Corp. 29
phpMyAdmin3.5.8に存在した正規表現インジェクション • テーブル名のプリフィックスを変更する処理に存在 // $from_prefix, $to_prefix, $currentは外部から操作可能 $newtablename = preg_replace("/^" . $from_prefix . "/", $to_prefix, $current); Copyright © 2016 HASH Consulting Corp. 30
攻撃ができる理由 case 'replace_prefix_tbl': $current = $selected[$i]; $newtablename = preg_replace("/^" . $from_prefix . "/", $to_prefix, $current); $from_pref = "/e\0" preg_replace("/^/e\0/", "phpinfo();", "test"); PHP5.4.3以前では、\0以降は無視される preg_replace("/^/e", "phpinfo();", "test"); Copyright © 2016 HASH Consulting Corp. 31
/e 修飾子… http://www.php.net/manual/ja/reference.pcre.pattern.modifiers.php 32
脆弱性が混入した要因 • preg_replaceに渡す正規表現をエスケープしていな かった – 最低限、/ をエスケープする必要がある…理論的には preg_replace(“/^” . $from_prefix . “/”, … ↓ reg_replace("/^" . preg_quote($from_prefix, '/') . "/", … • えーっと、preg_quoteって、マルチバイト対応だっ け? – Shift_JIS以外では問題ない? • 外部からの値を用いて正規表現を組み立てるべきで はない Copyright © 2016 HASH Consulting Corp. 33
PDOのDB接続時の文字エンコーディング 指定が可能に(PHP-5.3.6 2011/3/17) Copyright © 2016 Hiroshi Tokumaru 34
【文字コードの問題1】 5C問題によるSQLインジェクション • 5C問題とは – Shift_JIS文字の2バイト目に0x5Cが来る文字に起因する問 題 ソ、表、能、欺、申、暴、十 … など出現頻度の高い文字 が多い – 0x5CがASCIIではバックスラッシュであり、ISO-8859-1な ど1バイト文字と解釈された場合、日本語の1バイトが バックスラッシュとして取り扱われる – 一貫して1バイト文字として取り扱われれば脆弱性になら ないが、1バイト文字として取り扱われる場合と、 Shift_JISとして取り扱われる場合が混在すると脆弱性が発 生する Copyright © 2014 HASH Consulting Corp. 35
ソースコード(要点のみ)
<?php
header('Content-Type: text/html; charset=Shift_JIS');
$key = @$_GET['name'];
if (! mb_check_encoding($key, 'Shift_JIS')) {
die('文字エンコーディングが不正です');
}
// MySQLに接続(PDO)
$dbh = new PDO('mysql:host=localhost;dbname=books;charset=sjis',
'phpcon', 'pass1');
// Shift_JISを指定
$dbh->query("SET NAMES sjis");
// プレースホルダによるSQLインジェクション対策
$sth = $dbh->prepare("SELECT * FROM books WHERE author=?");
$sth->setFetchMode(PDO::FETCH_NUM);
// バインドとクエリ実行
$sth->execute(array($key));
?>
Copyright © 2014 HASH Consulting Corp.
36
5C問題によるSQLインジェクションの説明 Copyright © 2014 HASH Consulting Corp. 37
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. 38
サンプルコード
$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.
39
header関数のバグ修正 (PHP-5.4.0 2012/3/1) Copyright © 2016 Hiroshi Tokumaru 40
HTTPヘッダインジェクション 安全なウェブサイトの作り方改訂第7版より引用 41
このサンプルプログラムでどこまで悪用できるか? <?php header('Location: ' . $_GET['url']); Copyright © 2016 Hiroshi Tokumaru 42
ヘッダインジェクションによるXSSフィルタ回避
<?php
$cookie = $_GET['cookie'];
$txt = $_GET['txt'];
header('Content-Type: text/html; charset="UTF-8"');
header('Set-Cookie: A=' . $cookie);
?><!DOCTYPE html> <body><?php
echo "Cookie A=" . htmlspecialchars($_COOKIE['A']) ; ?>
<?php
echo $txt; // XSS脆弱性あり
?></body>
X-XSS-Protection: 0 ヘッダを追加して、XSSフィルタを無効にしたい…
Copyright © 2016 Hiroshi Tokumaru
43
header関数改修の歴史 • PHP 5.1.2 – 改行のチェックが追加される – %0d(キャリッジリターン)のチェックが漏れていた • PHP 5.3.11 および PHP 5.4.0 – キャリッジリターンのチェックが追加される – 継続行(下図)については許容 Location: http://php.net/ Set-Cookie: PHPSESSID=ABC; ↑空白またはタブ • PHP 5.4.38 / PHP 5.5.22 / PHP 5.6.6 – 継続行が禁止される Copyright © 2016 Hiroshi Tokumaru 44
Linuxディストリビューションの対応 ※CentOS5 ~ 7 / Ubuntu 14.04以前はパッチが出ていない Copyright © 2016 Hiroshi Tokumaru 45
安全なパスワード保存が簡単にできるよ うになった(PHP-5.5.0 2013/6/20) Copyright © 2016 Hiroshi Tokumaru 46
password_hash 関数 (PHP5.5から)
<?php echo password_hash('rasmuslerdorf’, PASSWORD_DEFAULT);
【結果】
$2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a
http://php.net/manual/ja/function.password-hash.php より引用
47
まとめ • 『例えば、PHPを避ける』の時代は、現実にPHPの 罠はかなりあった – 『避ける』必要はないにしても、気をつけなければ脆弱 性の原因に… • 2016年現在で、罠はかなり解消されつつある • 特に以下は重要 – Htmlspecialcharsの文字エンコーディングチェック – PDOのDB接続時の文字エンコーディング指定 • できるだけ新しいバージョンのPHPを使う • でも、もっと大切なことは、最新のパッチのあたっ たPHPを使うこと • PHPを使って、安全で素晴らしいサイト構築を! Copyright © 2016 Hiroshi Tokumaru 48
ご清聴ありがとうございました Copyright © 2016 Hiroshi Tokumaru 49