Contenu connexe Similaire à ウェブアプリケーションセキュリティ超入門 (20) Plus de Hiroshi Tokumaru (12) ウェブアプリケーションセキュリティ超入門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月)
– 技術士(情報工学部門)
2
5. Webサイトへの侵入経路
• 認証を突破される
– telnet, FTP, SSH等のパスワードを推測される
– FTP等のパスワードがマルウェア経由で漏洩する
• ソフトウェアの脆弱性を悪用される
– 基盤ソフトウェアの脆弱性を悪用される
• Apache, PHP, JRE(Java), Tomcat, …
• 脆弱性は世界中で調査され、日々新たな脆弱性が報告される
– アプリケーションの脆弱性を悪用される
• 個別のアプリケーションの脆弱性
• SQLインジェクションなど
5
7. 【参考】CVEとCWE
• 脆弱性の国際的な分類として CVE と CWE がある
• CVE(Common Vulnerabilities and Exposures)
– 個別ソフトウェアの具体的な脆弱性を識別する番号
– 米国政府の支援を受けた非営利団体のMITRE社が採番
– CVEの例
• CVE-2017-1001000 WordPress Rest APIの脆弱性
• CVE-2017-5638 Apache Struts2の脆弱性S2-045
• CWE(Common Weakness Enumeration)
– 脆弱性の種類を識別する番号
– MITRE社が中心となって策定
– CWEの例
• CWE-89 SQLインジェクション
• CWE-22 ディレクトリトラバーサル
7
参考: https://www.ipa.go.jp/security/vuln/CVE.html
https://www.ipa.go.jp/security/vuln/CWE.html
10. Java セキュアコーディングスタンダード CERT/Oracle 版
• はじめに
• 00. 入力値検査とデータの無害化 (IDS)
• 01. 宣言と初期化 (DCL)
• 02. 式 (EXP)
• 03. 数値型とその操作 (NUM)
• 04. オブジェクト指向 (OBJ)
• 05. メソッド (MET)
• 06. 例外時の動作 (ERR)
• 07. 可視性とアトミック性 (VNA)
• 08. ロック (LCK)
• 09. スレッド API (THI)
• 10. スレッドプール (TPS)
• 11. スレッドの安全性に関する雑則
(TSM)
• 12. 入出力 (FIO)
• 13. シリアライズ (SER)
• 14. プラットフォームのセキュリティ
(SEC)
• 15. 実行環境 (ENV)
• 49. 雑則 (MSC)
• AA. 参考情報
• BB. Glossary
10https://www.jpcert.or.jp/java-rules/
12. 12
どのような場所で検証?
エントリーポイント
ユーザによる入力
他システムとの連携ポイント
信頼境界
ここを超えるデータを検証 信頼境界
http://download.microsoft.com/download/3/e/e/3ee9501d-df73-41e9-baba-a1b4e41cb1ba/SecurityL100dataissue_6.ppt
先程の現実世界の例と同様に、すべ
ての入り口、つまりエントリポイン
トでデータを検証(チェック)すべ
きです。
たとえば、SQLインジェクションは
悪意のある、なしにかかわらず、
ユーザが入力したデータを検証せず
に、そのままSQL文の一部として利
用してしまった場合に発生します。
【中略】
一方、すでに検証済みの信頼のおけ
るデータはそのまま利用することが
できます。先程の例では、高い塀の
内側にある場合がそれです。この壁
を信頼境界といいます。
26. 「信頼されたデータ」が要求される例
• 信頼境界の中のデータ
– プログラムコード
– SQL文
– evalの入力
– 設定ファイル名に記載されたファイル名
– 正規表現
– オブジェクト
• アプリケーションで「信頼できることを確認」
– ログイン済みユーザ名(認証)
– 管理者が入力するSQL文(認可)
– CMSに入力するHTML(認可)
– 制限されたHTML(フィルタリング)
– 外部からのファイル名(basename)
26
30. 日本で一番売れているPHP教科書のサンプル
30
// ここまでで、認証済みであるこの検査が済んでいる
$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
文なので表示はない
たにぐちまこと、「よく分かるPHPの教科書」 P272より引用
31. エスケープしているのになぜ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が削除される
31
32. 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インジェクション
32
34. SQLインジェクション対策はプレースホルダで
• プレースホルダとは
SELECT * FROM books WHERE id=?
• 静的プレースホルダと動的プレースホルダ
– 静的: サーバー側で値をバインドする(エスケープは必要ない)
– 動的: 呼び出し側で値をエスケープしてバインドする
• 接続時に文字エンコーディングを指定する
$db = new PDO('mysql:host=myhost;dbname=mydb;charset=utf8',
DBUSER, DBPASS);
• 列の型を意識する
34
35. サンプルコード(PHP5.5.21以降)
// エミュレーションモードOFF = 静的プレースホルダ
$opt = array(PDO::ATTR_EMULATE_PREPARES => false,
// 「複文」を禁止する (PHP 5.5.21 / PHP 5.65 以降で有効)
PDO::MYSQL_ATTR_MULTI_STATEMENTS => false);
// 文字エンコーディング指定は PHP 5.3.7 以降
$db = new PDO('mysql:host=myhost;dbname=mydb;charset=utf8', DBUSER, DBPASS, $opt);
// プレースホルダを使って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();
※ 実際には try { … } catch … による例外処理を追加すること
35
38. 日本で一番売れているPHP教科書のサンプル
005: if (isset($_SESSION['id']) && $_SESSION['time'] + 3600 > time()) {
006: // ログインしている 省略
014: } else {
015: // ログインしていない
016: header('Location: login.php'); exit();
017: }
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: }
38
CSRF脆弱性
対策していない
続きはデモで
たにぐちまこと著「よくわかるPHPの教科書」より引用
41. トークンはどうやって生成する?
• ワンタイムトークンである必要はない
– セッション毎に固定で良い
– いわゆるワンタイムトークンだと処理が複雑になる
• セッション毎に一度「安全な乱数生成器」で生成する
– openssl_random_pseud_bytes() ← PHP5.3以降 オススメ
– random_bytes() ← PHP 7以降 オススメ
– /dev/urandom から読み込み (UNIX/Linux)
$rand = file_get_contents(‘/dev/urandom’, false, NULL, 0, 24);
$token = bin2hex($rand);
• type=hiddenなinputとセッションの両方に書いておいて、両者を比較
する。違っていたらエラー
41
43. XSSはなぜ危険か?
• XSSは、
– 利用者(被害者)のブラウザ上で
– 攻撃対象のドメイン(オリジン)で
– 攻撃者が自由にJavaScriptを実行できる
• これって、ウイルス?
– ウイルスではないが、結果としてウイルスと同じような被害
– XSSを悪用したウイルス(ワーム)はいくつかある
• ブラウザを乗っ取られたのと同じ
– 影響範囲はXSS脆弱性のあるページと同じドメイン(オリジン)
– 同一オリジン上はすべてのページが影響を受ける
※オリジン=ホスト名+スキーム+ポート番号
43
44. XSSのデモ
• 先ほどのCSRF対策済みの掲示板で、なりすまし書き込みをやってみる
• 実行するスクリプトは下記のもの
※ オリジナルの書籍にはこの脆弱性はありません
44
メモ
<script>
var s = document.body.innerHTML;
if (s.indexOf('徳丸浩さん、' + 'メッセージをどうぞ') >= 0 &&
s.indexOf('〇〇小学校を襲' + '撃します') < 0) {
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>
45. XSSの対策
• 文脈に応じてHTMLエスケープ
– エスケープするタイミングは表示の直前
– PHPの場合、htmlspecialcharsの引数に注意
• 第2引数は、文脈により変えるか、ENT_QUOTES固定
• 第3引数はPHP内部の文字エンコーディングを指定
PHP5.3までのデフォルトは ISO-8859-1
PHP5.4, 5.5のデフォルトは UTF-8
PHP5.6からはデフォルトとして default_charset
• 属性値はダブルクオートで囲む
• レスポンスヘッダで文字エンコーディングを指定
– Content-Type: text/html; charset=UTF-8
• JavaScriptの動的生成を避ける
– HTML上に値を書いてJavaScriptから参照
45
47. 日本で一番売れているPHP教科書のサンプル(ユーザ登録)
join/index.php
20: $fileName = $_FILES['image']['name'];
21: if (!empty($fileName)) {
22: $ext = substr($fileName, -3);
23: if ($ext != 'jpg' && $ext != 'gif') {
24: $error['image'] = 'type';
25: }
26: }
40: if (empty($error)) {
41: // 画像をアップロードする
42: $image = date('YmdHis') . $_FILES['image']['name'];
43: move_uploaded_file($_FILES[‘image’][‘tmp_name’], ‘../member_picture/’ . $image);
44: $_SESSION['join'] = $_POST;
46: $_SESSION['join']['image'] = $image;
join/check.php
58: <img src="../member_picture/<?php echo $_SESSION['join']['image']; ?>" … />
47たにぐちまこと著「よくわかるPHPの教科書」より引用
ファイル名をHTMLエスケープしないで表示
アップロードファイルをドキュメントルート下に保存
ファイル名の末尾3文字(拡張子)のみ確認
50. IE / Edge なら攻撃できる
以下のHTTPリクエストを送信させる必要があるが…
------WebKitFormBoundaryj4Hx293np6E9kb7T
Content-Disposition: form-data; name=“foo”; filename="攻撃文字列"
1
------WebKitFormBoundaryj4Hx293np6E9kb7T--
Content-Disposition: form-data; name="foo“; filename="攻撃文字列"
<select name=‘foo“; filename=”攻撃文字列’> ← 左のようにすると…
------WebKitFormBoundaryj4Hx293np6E9kb7T
Content-Disposition: form-data; name="foo" filename="攻撃文字列"
50
※ この攻撃方法は三井物産セキュアディレクションの望月岳氏にご教授いただきました