1.6K Views
June 28, 14
スライド概要
PHP カンファレンス関西 2014 での講演資料です
http://conference.kphpug.jp/2014/
2014年6月28日(土)
大阪産業創造館
安全なPHPアプリケーションの作り方2014 2014年6月28日 HASHコンサルティング株式会社 徳丸 浩
徳丸浩の自己紹介 • 経歴 – 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 © 2008-2014 HASH Consulting Corp. All rights reserved 2
アジェンダ • アプリケーションの脆弱性を対策しよう – クロスサイト・リクエストフォージェリ(CSRF) – クロスサイト・スクリプティング(XSS) – SQLインジェクション • プラットフォームの脆弱性を対策しよう – PHP自身の脆弱性対策 – SNSやphpMyAdminの脆弱性対策 • パスワードを保護しよう – ソルトつきHASHでのパスワード保存 Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 3
アプリケーションの脆弱性を対 策しよう 4
クロスサイト・リクエストフォージェリ(CSRF) 5
CSRFはなぜ危険か? • CSRFとは… – FORMのACTIONってクロスドメインで指定できるよね – だから、FORMを踏ませる罠が作れるよね – 投稿、パスワード変更、購入、設定変更… – 攻撃者はFORMの実行結果は見られないので、DB更新や削除など が問題となる • 最悪ケースの危険性は、SQLインジェクションやXSSほ どではない • 脆弱性のあるページの機能の悪用にとどまる • 脆弱性の発見が容易 • 脆弱性の悪用が容易 • 実際に悪用されている Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 6
横浜市のCSRF悪用の犯行予告手口(推測) http://d.hatena.ne.jp/Kango/20121008/1349660951 より引用 7
日本で一番売れているPHP教科書のサンプル
005: if (isset($_SESSION['id']) && $_SESSION['time'] + 3600 > time()) {
006:
// ログインしている 省略
014: } else {
015:
// ログインしていない
016:
header('Location: login.php'); exit();
017: }
CSRF脆弱性
018:
019: // 投稿を記録する
対策していない
020: if (!empty($_POST)) {
021:
if ($_POST['message'] != '') {
022:
$sql = sprintf('INSERT INTO posts SET member_id=%d,
message="%s", reply_post_id=%d, created=NOW()',
023:
mysql_real_escape_string($member['id']),
024:
mysql_real_escape_string($_POST['message']),
025:
mysql_real_escape_string($_POST['reply_post_id'])
026:
);
027:
mysql_query($sql) or die(mysql_error());
続きはデモで
028:
029:
header('Location: index.php'); exit();
030:
}
031: }
8
CSRFによる成りすまし投稿 ③被害者のブラウザ経由で 掲示板に書き込み 掲示板の書き込みログ は被害者のIPアドレス 9
CSRF対策の方法は? • 色々な対策が知られている • トークンによるもの • 再認証 • Refererのチェック • Captcha • … • 実際は、トークン一択と考えて良い Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 10
トークンはどうやって生成する? • ワンタイムトークンである必要はない – セッション毎に固定で良い – いわゆるワンタイムトークンだと処理が複雑になる • セッション毎に一度「安全な乱数生成器」で生成する – openssl_random_pseud_bytes() ← PHP5.3以降 オススメ – /dev/urandom から読み込み (UNIX/Linux) $rand = file_get_contents(‘/dev/urandom’, false, NULL, 0, 24); $token = bin2hex($rand); • type=hiddenなinputとセッションの両方に書いておい て、両者を比較する。違っていたらエラー Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 11
クロスサイト・スクリプティング(XSS) 12
XSSはなぜ危険か? • XSSは、 – 利用者(被害者)のブラウザ上で – 攻撃対象のドメイン(オリジン)で – 攻撃者が自由にJavaScriptを実行できる • これって、ウイルス? – ウイルスではないが、結果としてウイルスと同じような被害 – XSSを悪用したウイルス(ワーム)はいくつかある • ブラウザを乗っ取られたのと同じ – 影響範囲はXSS脆弱性のあるページと同じドメイン(オリジン) – 同一オリジン上はすべてのページが影響を受ける ※オリジン=ホスト名+スキーム+ポート番号 Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 13
XSSのデモ
• 先ほどのCSRF対策済みの掲示板で、なりすまし書き込み
をやってみよう
• 実行するスクリプトは下記のもの
<script>
var token = document.getElementsByName('token')[0].value;
var req = new XMLHttpRequest();
req.open('POST', 'index.php');
req.setRequestHeader('Content-Type',
'application/x-www-form-urlencoded');
data = 'message=ぼくはまちちゃん!こんにちはこんにちは!!&token=‘
+ token;
req.send(data);
</script>
Copyright © 2008-2014 HASH Consulting Corp. All rights reserved
14
XSSの対策 • 文脈に応じてHTMLエスケープ – エスケープするタイミングは表示の直前 – htmlspecialcharsの引数に注意 • 第2引数は、文脈により変えるか、ENT_QUOTES固定 • 第3引数はPHP内部の文字エンコーディングを指定 PHP5.3までのデフォルトは ISO-8859-1 PHP5.4, 5.5のデフォルトは UTF-8 PHP5.6からはデフォルトとして default_charset • 属性値はダブルクオートで囲む • レスポンスヘッダで文字エンコーディングを指定 – header('Content-Type: text/html; charset=UTF-8'); • JavaScriptの動的生成を避ける – HTML上に値を書いてJavaScriptから参照 Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 15
SQLインジェクション 16
SQLインジェクションはなぜ危険か? • 攻撃対象サーバーのデータベースについて、攻撃者が自 由にSQL呼び出しができる • データベースの中身の漏洩、改ざんが起こる • 脆弱性のあるテーブルだけでなく、データベース内のす べてのデータが漏洩できる。場合によっては改ざんも • 使い勝手の良い攻撃ツール(SQL注入工具:名目は診断ツ ール)が安価で流通している • SQL注入工具はExcel使うより簡単だよ Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 17
日本で一番売れているPHP教科書のサンプル // ここまでで、認証済みであるこの検査が済んでいる $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’]) { // 投稿者の確認 ここにSQLインジェクション // 投稿した本人であれば、削除 mysql_query('DELETE FROM posts WHERE id=' . mysql_real_escape_string($id)) or die(mysql_error()); } しかし、DELETE FROM文 なので表示はない 18
エスケープしているのになぜSQLインジェクション? $id = ’88-1’; で説明。これはエスケープ後も 88-1 $sql = sprintf('SELECT * FROM posts WHERE id=%d', mysql_real_escape_string($id)); ↓ 生成されるSQL文は、88-1が%dの整数化で88になるので SELECT * FROM posts WHERE id=88 mysql_query(‘DELETE FROM posts WHERE id=’ . mysql_real_escape_string($id)); ↓ 生成されるSQL文は、エスケープ後も 88-1 のままなので DELETE FROM posts WHERE id=88-1 ※チェックはid=88で、削除されるのはid=88-1 すなわち、87が削除 される Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 19
SQL文のエラーが起こるか否かで情報を盗む • SQLインジェクションにより実行されるSQL文の例 DELETE FROM posts WHERE id=88-(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句の中 88-(SELECT … WHERE …) • 中のWHERE句は LIKE 述語にESCAPE句がある • ESCAPE句はIF関数により、membersの1行目の1文字目が ’M’以上の場合’a’、それ以外の場合’ab’ • SQL文の文法上、ESCAPE句は1文字以外だとエラー • この結果を繰り返すことによって、対象文字列を絞り込む →ブラインドSQLインジェクション 続きはデモで 20
SQLインジェクション対策はプレースホルダで • プレースホルダとは SELECT * FROM books WHERE id=? • 静的プレースホルダと動的プレースホルダ – 静的: サーバー側で値をバインドする(エスケープは必要な い) – 動的: 呼び出し側で値をエスケープしてバインドする • 接続時に文字エンコーディングを指定する $db = new PDO('mysql:host=myhost;dbname=mydb;charset=utf8', DBUSER, DBPASS); • 列の型を意識する Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 21
サンプルコード
$db = new PDO('mysql:host=myhost;dbname=mydb;charset=utf8',
DBUSER, DBPASS);
// エミュレーションモードOFF = 静的プレースホルダ
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// プレースホルダを使ってSQLを準備
$prepare = $db->prepare('SELECT * FROM example WHERE id
= :id and language = :lang');
// 型を指定してbind (キャストはPDOのバグ対策)
$prepare->bindValue(':id', (int) $int, PDO::PARAM_INT);
$prepare->bindValue(':lang', $str, PDO::PARAM_STR);
$prepare->execute();
http://blog.a-way-out.net/blog/2013/12/18/pdo-preparestatement-numeric-literal-part2/ を参考せさせていただきました 22
プラットフォームの脆弱性を対 策しよう 23
PHPの脆弱性にどうつきあう? 24
例えば、PHPを避ける 出典: IPA「セキュアプログラミング講座」 25
PHP自身の脆弱性対策の考え方 • 「PHPを避ける」という意見は下記による – PHP自体に脆弱性が多い – register_globalsのような脆弱性を生みやすい機構がある • しかし、いずれも解消されており、「PHPに脆弱性が多 い」というのは昔の話…神話のようなもの • PHPのバージョンアップの度にがっかりほっとしている • しかし、たまにすごい脆弱性が出るのも事実 – 例: PHP-CGI脆弱性(CVE-2012-1823) – どの言語でもあり得ることですけどね • 脆弱性対処の基本は PHP自体のバージョンアップだが… • 実は、それが一番大変 26
PHPのリリースサイクル • Yearly release cycle – 3 years release life cycle – 2 years bug fixes only – 1 year security fixes only • 下図のように、PHP5.x(2番目の数字)を使い続けるのは、 2~3年が限度 Copyright © 2008-2014 HASH Consulting Corp. All rights reserved https://wiki.php.net/rfc/releaseprocess より引用 27
PHPのバージョンアップにつきあうには? • サイトリリース前にバージョンアップ方針を決める • 基本的な選択肢 – PHPのバージョンアップにとことん付き合う – 影響を受ける脆弱性がある場合のみバージョンアップする – LinuxディストリビューションのパッケージとしてPHPを導入し て、パッチを適用する • PHP以外に、フレームワークやDB、ライブラリ等も • サポート計画を検討する – PHPをRedHat/CentOSのパッケージで導入すれば10年サポート – Redhat5/Centos5のPHP5.1.6もメンテナンスされているよ ※2017年3月31日まで – 途中でメジャーバージョンアップする計画を立てておく • 他のソフトウェアのサポートライフサイクルにも注意 Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 28
SNSやphpMyAdminの脆弱性対策 29
30 http://www.nict.go.jp/info/topics/2012/05/announce120516.html より引用
31
なにがあったか • 2012年3月15公開のJoomla! 2.5.3で修正された脆弱性 • NICTが攻撃されたのは5月1日 なので、脆弱性公開から1.5 ヶ月で攻撃を受けたことにな る • ユーザ登録時に、特殊な操作 により、自ら管理者権限を得 ることができる • 続きはデモで 32
パスワードを保護しよう ~ソルトつきHASHでのパスワード保存~ 33
LinkedInからのパスワード漏洩事件 http://www.itmedia.co.jp/enterprise/articles/1206/07/news017.html より引用 Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 34
650万件のパスワードハッシュのうち540万件が1週間で解読 https://twitter.com/jmgosney/statuses/213212108924522496 より引用 Surviving on little more than furious passion for many sleepless days, we now have over 90% of the leaked passwords recovered. http://securitynirvana.blogspot.jp/2012/06/final-word-on-linkedin-leak.html Copyright © 2008-2014 HASH Consulting Corp. All rights reserved より引用 35
Saltってなに? • ソルト(Salt)とは、ハッシュの元データ(パスワード)に追加す る文字列 • 見かけのパスワードの長さを長くする →レインボーテーブル対策 • ユーザ毎にソルトを変えることで、パスワードが同じでも、異なる ハッシュ値が得られる – ソルトがない場合、パスワードの組み合わせ回数分ですむが、ソルト があると、×ユーザ数 に試行回数が増える – LinkedInの場合は、試行回数が 650万倍に ! • ソルトの要件 – ある程度の長さを確保すること – ユーザ毎に異なるものにすること • ソルトには乱数を用いることが多いが、乱数が必須というわけでは ない(暗号論的に安全な乱数である必要はもちろんない) • ソルトは秘密情報ではない。ソルトは、通常ハッシュ値と一緒に保 存する Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 36
Stretchingってなに? • ストレッチング(Stretching)とは、ハッシュの計算を繰り返すこと • ハッシュの計算を遅くすることにより、辞書攻撃や総当たり攻撃に対 抗する • 1万回ストレッチすると、「 GPUモンスターマシンで20分掛かる」が 20万分になる計算 むむ、これだとパスワードの定期的変更か? – 20万分 = 139日 … • 「悪い」パスワードまで救えるわけではない – 「password」というパスワードをつけていたら、100万回ストレッチして もすぐに解読されてしまう • 十分長いパスワードをつけてもらえば、ストレッチングは必要ない – 1文字パスワードを長くすることは、約90回のストレッチングに相当する。 パスワードを2文字長くしてもらえば… – ストレッチングは、「弱いパスワード」の救済の意味がある • ストレッチングはメリットとデメリットがあるので、導入の有無と回 数をよく検討すること Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 37
パスワードをハッシュで保存する場合の課題 • 「パスワードリマインダ」が実装できない – 「秘密の質問」に答えるとパスワードを教えてくれるアレ – パスワードリセットで代替 • ハッシュの形式(アルゴリズム、ソルト、ストレッチ回数)の変更 – 生パスワードが分からないのでハッシュの方式変更がバッチ処理では できない – ユーザの認証成功の際にはパスワードが分かるので、その際に方式を 変更すると良い – 緊急を要する場合は、現在パスワードを無効にして、パスワードリセ ットで – ハッシュ値あるいは別フィールドに「方式番号」をもっておく – PHP5.5のpassword_hash関数が便利 (ソルト・ストレッチング内 蔵) $1$d641fdabf96912$4b3c3e95dfab179ebfef220172f58171 方式番号 ソルト ハッシュ値 Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 38
password_hash 関数 (PHP5.5から)
<?php echo password_hash('rasmuslerdorf’, PASSWORD_DEFAULT);
【結果】
$2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a
http://php.net/manual/ja/function.password-hash.php
Copyright © 2008-2014 HASH Consulting Corp. All rights reserved より引用
39
いろいろ言いましたが… Copyright © 2008-2014 HASH Consulting Corp. All rights reserved 40
詳細は徳丸本を読みましょう 2011年3月5日初版第1刷 2011年7月28日 初版第4刷 41
Thank you 42