『例えば、PHPを避ける』以降PHPはどれだけ安全になったか

52,755 views

Published on

PHPカンファレンス北海道2016基調講演

Published in: Technology

『例えば、PHPを避ける』以降PHPはどれだけ安全になったか

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

×