2.7K Views
September 02, 17
スライド概要
OWASP Nagoya Local Chapter Meeting 1st 講演資料です
https://owaspnagoya.connpass.com/event/59813/
ウェブアプリケーションセキュリティ超入門 EG セキュアソリューションズ 徳丸 浩
徳丸浩の自己紹介 • 経歴 – 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
アジェンダ • • • • • • 侵入を受けるウェブサイトとはどういう状況なのか? 入力値の問題 SQLインジェクション クロスサイト・リクエストフォージェリ(CSRF) クロスサイト・スクリプティング(XSS) アップロードの問題 3
侵入を受けるウェブサイトとはどういう状況 なのか? 4
Webサイトへの侵入経路 • 認証を突破される – telnet, FTP, SSH等のパスワードを推測される – FTP等のパスワードがマルウェア経由で漏洩する • ソフトウェアの脆弱性を悪用される – 基盤ソフトウェアの脆弱性を悪用される • Apache, PHP, JRE(Java), Tomcat, … • 脆弱性は世界中で調査され、日々新たな脆弱性が報告される – アプリケーションの脆弱性を悪用される • 個別のアプリケーションの脆弱性 • SQLインジェクションなど 5
脆弱性とは何か • 脆弱性とは、悪用可能なバグ、設定不備など • 悪用可能なバグの例 – バッファオーバーフロー – SQLインジェクション – クロスサイト・スクリプティング • 悪用可能な設定不備の例 – – – – デフォルトのパスワードのまま放置している パスワードとして「password」を設定している 任意のメールを中継する設定(オープンリレー) インターネット経由で誰でも使えるPROXY 6
【参考】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 ディレクトリトラバーサル 参考: https://www.ipa.go.jp/security/vuln/CVE.html https://www.ipa.go.jp/security/vuln/CWE.html 7
CWEの脆弱性タイプの階層構造図 https://www.ipa.go.jp/security/vuln/CWE.html より引用 8
CWE-20 不適切な入力確認 9
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 https://www.jpcert.or.jp/java-rules/ 10
IDS00-J. 信頼境界を越えて渡される信頼できないデータは無害化する 多くのプログラムは、認証済みでないユーザやネットワーク接続等、信頼できない情 報源からデータを受け取り、それを(改変したり、あるいはそのまま)信頼境界 (trust boundary)を越えて、信頼される側に渡す。多くの場合、データは、一定のシ ンタックスを持つ文字列であり、プログラム内部のサブシステムによって解析される。 不正な形式の入力データには対応できないかもしれないし、インジェクション攻撃が 含まれているかもしれないため、そのような入力データは無害化(sanitize)しなくて はならない。 特にコマンドインタプリタやパーサに渡される文字列データはすべて、解析される文 脈で無害な状態(innocuous)にしなければならない。 コマンドインタプリタやパーサの多くは、独自の無害化メカニズムや検査機構を備 えている。可能であれば、それらの無害化メカニズムを使用するほうが、独自に無害 化メカニズムを実装するよりも好ましい。独自に実装した無害化メカニズムでは、特 殊なケースやパーサの複雑な内部構造に配慮しない実装を行ってしまう可能性がある。 それだけでなく、コマンドインタプリタやパーサに新しい機能が追加されたとき、無 害化メカニズムが適切にメンテナンスされない恐れもある。 アーカイブ http://web.archive.org/web/20150515043831/ https://www.jpcert.or.jp/java-rules/ids00-j.html 11
どのような場所で検証? エントリーポイント ユーザによる入力 他システムとの連携ポイント 信頼境界 ここを超えるデータを検証 信頼境界 先程の現実世界の例と同様に、すべ ての入り口、つまりエントリポイン トでデータを検証(チェック)すべ きです。 たとえば、SQLインジェクションは 悪意のある、なしにかかわらず、 ユーザが入力したデータを検証せず に、そのままSQL文の一部として利 用してしまった場合に発生します。 【中略】 一方、すでに検証済みの信頼のおけ るデータはそのまま利用することが できます。先程の例では、高い塀の 内側にある場合がそれです。この壁 を信頼境界といいます。 http://download.microsoft.com/download/3/e/e/3ee9501d-df73-41e9-baba-a1b4e41cb1ba/SecurityL100dataissue_6.ppt 12
こうですか、わかりません (>_<)/ 信頼境界を越えて渡される信頼できないデータは無害化する 信頼境界 信頼できないデータ 無害化 フィルタ 無害なデータ SQL 呼び出し 13
こうですか、わかりません (>_<)/ 信頼境界を越えて渡される信頼できないデータは無害化する 信頼境界 信頼できないデータ 無害化 フィルタ 無害なデータ SQL 呼び出し 14
IDS00-J. 信頼境界を越えて渡される信頼できないデータは無害化する 多くのプログラムは、認証済みでないユーザやネットワーク接続等、信頼できない情 報源からデータを受け取り、それを(改変したり、あるいはそのまま)信頼境界 (trust boundary)を越えて、信頼される側に渡す。多くの場合、データは、一定のシ ンタックスを持つ文字列であり、プログラム内部のサブシステムによって解析される。 不正な形式の入力データには対応できないかもしれないし、インジェクション攻撃が 含まれているかもしれないため、そのような入力データは無害化(sanitize)しなくて はならない。 特にコマンドインタプリタやパーサに渡される文字列データはすべて、解析される文 脈で無害な状態(innocuous)にしなければならない。 コマンドインタプリタやパーサの多くは、独自の無害化メカニズムや検査機構を備 えている。可能であれば、それらの無害化メカニズムを使用するほうが、独自に無害 化メカニズムを実装するよりも好ましい。独自に実装した無害化メカニズムでは、特 殊なケースやパーサの複雑な内部構造に配慮しない実装を行ってしまう可能性がある。 それだけでなく、コマンドインタプリタやパーサに新しい機能が追加されたとき、無 害化メカニズムが適切にメンテナンスされない恐れもある。 アーカイブ http://web.archive.org/web/20150515043831/ https://www.jpcert.or.jp/java-rules/ids00-j.html 15
IDS00-J. 信頼境界を越えて渡される信頼できないデータは無害化する 違反コード 以下の違反コード例は、ユーザ認証を行うJDBCのコードを示している。パスワードは char型配列として渡され、データベースへの接続が作成され、パスワードがハッシュ 化されている。 残念ながらこのコードはSQLインジェクション攻撃を許してしまう。SQL文 sqlString は無害化されていない入力値を受け付けており、前述の攻撃シナリオが成立してしま うだろう。 適合コード (PreparedStatement) 幸いJDBCライブラリはSQLコマンドを組み立てるAPIを提供しており、信頼できない データを無害化してくれる。java.sql.PreparedStatementクラスは入力文字列を適切に エスケープ処理するため、適切に利用すればSQLインジェクション攻撃を防ぐことが できる。これはコンポーネントベースで行う無害化の一例である。 この適合コードでは java.sql.Statement の代わりに PreparedStatementを使用するよう に doPrivilegedAction() メソッドを変更している。また、引数 username の長さを検 証しており、攻撃者が任意に長いユーザ名を送り込むことを防止している。 アーカイブ http://web.archive.org/web/20150515043831/ https://www.jpcert.or.jp/java-rules/ids00-j.html 16
こうだった 信頼境界を越えて渡される信頼できないデータは無害化する 信頼境界 プレースホルダ 信頼できないデータ 安全な形でSQL 呼び出し 17
そもそも信頼境界関係なくね? 18
そう、故に見出しが改定された 19
まさかの「SQLインジェクションを防ぐ」 信頼境界も、信頼できないも、無害化も タイトルから消えた https://www.jpcert.or.jp/java-rules/ids00-j.html 20
では、信頼境界は無意味? 21
そうでもない 22
こういうのはダメ hiddenパラメータでSQL文を渡している 信頼境界 hiddenパラメータで SQL文を渡している SQL呼び出し 23
phpMyAdminの場合 DB管理者 信頼境界 認証・認可 SQL呼び出し SQL文 信頼できる情報源 SQL文 SQL文 ソースコード セッション変数 データベース 設定ファイル … 24
「安全なウェブサイトの作り方」に出てくる信頼境界の例 • SQLインジェクション – ウェブアプリケーションに渡されるパラメータにSQL文を直接指定しない。 • OSコマンドインジェクション – Perlのopen関数は、引数として与えるファイルパスに「|」(パイプ)を使うことでOS コマンドを実行できるため、外部からの入力値を引数として利用する実装は危険です • ディレクトリトラバーサル – 外部からのパラメータでウェブサーバ内のファイル名を直接指定する実装を避ける – ファイルを開く際は、固定のディレクトリを指定し、かつファイル名にディレクトリ 名が含まれないようにする。 • XSS – 入力されたHTMLテキストから構文解析木を作成し、スクリプトを含まない必要な要 素のみを抽出する – 入力されたHTMLテキストから、スクリプトに該当する文字列を排除する 25
「信頼されたデータ」が要求される例 • 信頼境界の中のデータ – – – – – – プログラムコード SQL文 evalの入力 設定ファイル名に記載されたファイル名 正規表現 オブジェクト • アプリケーションで「信頼できることを確認」 – – – – – ログイン済みユーザ名(認証) 管理者が入力するSQL文(認可) CMSに入力するHTML(認可) 制限されたHTML(フィルタリング) 外部からのファイル名(basename) 26
信頼に関して… • 安全に使う方法があれば「信頼」を気にしない – SQL文のプレースホルダにバインドする値 – シェルを経由しないコマンド呼び出しのパラメータ – 安全なメール送信APIに渡すメールアドレス • 信頼するしかないデータもある – プログラムコード、SQL文そのもの – evalの入力、正規表現 – オブジェクト • 認証やフィルタリングにより「信頼」できる場合 – basename関数を通したファイル名 – 認証されたユーザ名 – 権限を認可されたSQL文、HTMLテキスト 27
SQLインジェクション攻撃 28
今日はこの本を題材として 使います 29
日本で一番売れている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’]) { // 投稿者の確認 // 投稿した本人であれば、削除 mysql_query('DELETE FROM posts WHERE id=' . mysql_real_escape_string($id)) or die(mysql_error()); } ここにSQLインジェクション しかし、DELETE FROM 文なので表示はない たにぐちまこと、「よく分かるPHPの教科書」 P272より引用 30
エスケープしているのになぜ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
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
SQLインジェクションはなぜ危険か? • 攻撃対象サーバーのデータベースについて、攻撃者が自由にSQL呼び 出しができる • データベースの中身の漏洩、改ざんが起こる • 脆弱性のあるテーブルだけでなく、データベース内のすべてのデータ が漏洩できる。場合によっては改ざんも • 使い勝手の良い攻撃ツール(SQL注入工具:名目は診断ツール)が安価 で流通している • SQL注入工具はExcel使うより簡単だよ 33
SQLインジェクション対策はプレースホルダで • プレースホルダとは SELECT * FROM books WHERE id=? • 静的プレースホルダと動的プレースホルダ – 静的: サーバー側で値をバインドする(エスケープは必要ない) – 動的: 呼び出し側で値をエスケープしてバインドする • 接続時に文字エンコーディングを指定する $db = new PDO('mysql:host=myhost;dbname=mydb;charset=utf8', DBUSER, DBPASS); • 列の型を意識する 34
サンプルコード(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
成りすまし犯行予告…ただし、マルウェアではなく、CSRF脆弱 性でやってみる 横浜市の事 例パターン 36
横浜市のCSRF悪用の犯行予告手口(推測) http://d.hatena.ne.jp/Kango/20121008/1349660951 より許諾を得て引用 37
日本で一番売れているPHP教科書のサンプル
005: if (isset($_SESSION['id']) && $_SESSION['time'] + 3600 > time()) {
006:
// ログインしている 省略
014: } else {
015:
// ログインしていない
016:
header('Location: login.php'); exit();
017: }
018:
CSRF脆弱性
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: }
たにぐちまこと著「よくわかるPHPの教科書」より引用
38
CSRFによる成りすまし投稿 ③被害者のブラウザ経由 で掲示板に書き込み 掲示板の書き込みログは 被害者のIPアドレス 39
CSRF対策の方法は? • • • • 色々な対策が知られている トークンによるもの 再認証 Refererのチェック • Captcha • … • 実際は、トークン一択と考えて良い 40
トークンはどうやって生成する? • ワンタイムトークンである必要はない – セッション毎に固定で良い – いわゆるワンタイムトークンだと処理が複雑になる • セッション毎に一度「安全な乱数生成器」で生成する – 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
クロスサイト・スクリプティング(XSS) 42
XSSはなぜ危険か? • XSSは、 – 利用者(被害者)のブラウザ上で – 攻撃対象のドメイン(オリジン)で – 攻撃者が自由にJavaScriptを実行できる • これって、ウイルス? – ウイルスではないが、結果としてウイルスと同じような被害 – XSSを悪用したウイルス(ワーム)はいくつかある • ブラウザを乗っ取られたのと同じ – 影響範囲はXSS脆弱性のあるページと同じドメイン(オリジン) – 同一オリジン上はすべてのページが影響を受ける ※オリジン=ホスト名+スキーム+ポート番号 43
XSSのデモ
• 先ほどのCSRF対策済みの掲示板で、なりすまし書き込みをやってみる
• 実行するスクリプトは下記のもの
※ オリジナルの書籍にはこの脆弱性はありません
メモ
<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>
44
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
ファイルアップロードに関連して 46
日本で一番売れている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: }
ファイル名の末尾3文字(拡張子)のみ確認
アップロードファイルをドキュメントルート下に保存
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;
ファイル名をHTMLエスケープしないで表示
join/check.php
58: <img src="../member_picture/<?php echo $_SESSION['join']['image']; ?>" … />
たにぐちまこと著「よくわかるPHPの教科書」より引用
47
「よくわかるPHPの教科書」のアップロードにはいくつかの問題がある • 画像ファイル名をエスケープしないで表示している…潜在的なXSS • アップロード画像をドキュメントルート下に保存 – PHP等のファイルをアップロードされる可能性 – 拡張子(末尾3文字)のチェックはしているが… • ドットのチェックをしていないので foogif みたいなファイル名も許容 – 抜けはないのか? • アップロード後のファイル名が、日付日時 + 元ファイル名 という採 番ルール – 同時刻に同名のファイルをアップロードされたらファイル名が衝突する 48
本当に「潜在的な」XSSなのか? • ファイルアップロードフォームで、ファイル名を外部から指定する方 法はあるか? <input type="file" name="image" size="35" value="test" /> 49
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
XSS発火用スクリプト
<form enctype=“multipart/form-data"
action="http://park.jp/minibbs/join/index.php" method="POST">
<input name="name" value="taro"><br>
<input name="email" value="[email protected]"><br>
<input name="password" value="123456"><br>
<select name='image";
filename="¥"><img src=a onerror=alert(document.cookie)>.gif'>
<option value="<?php phpinfo(); ?>" selected>1</option>
</select>
<input type="submit" value="submit" />
</form>
51
PHPスクリプトをアップロードされる心配は? • PHP等のスクリプトをアップロードされる危険がある • 拡張子(末尾3文字)が jpg または gif に限定されているので、拡張 子 .php のファイルは弾かれる…はずだが • Apacheの設定で foo.php.gif 等の二重拡張子が有効な場合 • PHPスクリプトを実行される余地がある 52
む、難しい… • PHP入門書として、あまり難しいことも書けないのではないか? • 実は、多くの脆弱性は、局所的に対策をとっておけば避けられる • 以下のような中途半端な「判断」が危ない – ファイル名文字列だからXSSはできない • 局所的な対策を積み重ねること • 同じ処理は同じように書きましょぅ – チェックと更新で条件が違うのは脆弱性の元 – WordPress REST APIのCVE-2017-1001000 もこれが原因 53
まとめ • • • • • • 侵入を受けるウェブサイトとはどういう状況なのか? 入力値の問題 SQLインジェクション クロスサイト・リクエストフォージェリ(CSRF) クロスサイト・スクリプティング(XSS) アップロードの問題 54