2.1K Views
December 13, 17
スライド概要
セキュリティ・ミニキャンプ in 近畿 2017(神戸)における講演です
http://www.security-camp.org/minicamp/kobe2017.html
Webアプリセキュリティの常識 EGセキュアソリューションズ株式会社 徳丸浩
アジェンダ • Webサイト攻撃のトレンドと対処法 • 攻撃の典型例 – SQLインジェクション攻撃 – CSRF – クロスサイトスクリプティング(XSS) – クリックジャッキング • 今時のウェブサイトの守り方 2
徳丸浩の自己紹介 • 経歴 – 1985年 京セラ株式会社入社 – 1995年 京セラコミュニケーションシステム株式会社(KCCS)に出向・転籍 – 2008年 KCCS退職、HASHコンサルティング株式会社(現社名:EGセキュアソリューショ ンズ株式会社)設立 • 経験したこと – 京セラ入社当時はCAD、計算幾何学、数値シミュレーションなどを担当 – その後、企業向けパッケージソフトの企画・開発・事業化を担当 – 1999年から、携帯電話向けインフラ、プラットフォームの企画・開発を担当 Webアプリケーションのセキュリティ問題に直面、研究、社内展開、寄稿などを開始 – 2004年にKCCS社内ベンチャーとしてWebアプリケーションセキュリティ事業を立上げ • 現在 – EGセキュアソリューションズ株式会社 代表 https://www.eg-secure.co.jp/ – 独立行政法人情報処理推進機構 非常勤研究員 https://www.ipa.go.jp/security/ – 著書「体系的に学ぶ 安全なWebアプリケーションの作り方」(2011年3月) 「徳丸浩のWebセキュリティ教室 」(2015年10月) – 技術士(情報工学部門) 3
Webサイト攻撃のトレンドと対処法 4
Webサイトに対する侵入事件のトレンド • 2000年:各省庁を狙った改ざん事件 – 「ファイアウォールが導入されていなかった」という報道から、FW導入の きっかけに • 2005年:カカクコム、ワコールなどに対するSQLインジェクション攻撃 (理論的可能性から実際の脅威に) • 2008年:SQLインジェクション攻撃が急増 • 2009年~2010年:Gumblar騒動 • 2011年:PSN事件、ソニー関連会社への攻撃、Lizamoon攻撃(SQLイン ジェクション) • 2012年:Anonymous、国際情勢がらみの攻撃 • 2013年:パスワードリスト攻撃など不正ログインの多発 • 2014年:Heartbleed、Strutsの脆弱性等 • 2015年:Joomlaの脆弱性CVE-2015-8562に対する攻撃 • 2017年:WordPress REST APIの脆弱性、Struts2 S2-045脆弱性に対する 攻撃 5
セキュリティはイタチごっこと言うけれど • 2000年頃の侵入事件は、以下が多いと推測 – ローカルネットワーク向けのポート(RPC等)に対する攻撃 – 管理用 telnet / ftp / ssh に対する辞書攻撃 – Apache や sendmail の脆弱性悪用 • 上記が対策された後に、SQLインジェクションの全盛期 (2005年~2010年頃) • SQLインジェクションが対策されてくると下記が台頭 – フレームワーク(Struts2、Ruby on Rails等)の脆弱性を狙った攻撃 – 著名アプリケーション(WordPress等)の脆弱性を狙った攻撃 – 管理端末のマルウェア感染(端末脆弱性や管理者の不注意) (いわゆるガンブラー、標的型攻撃) – ユーザーパスワードに対する攻撃 (フィッシング、パスワードリスト攻撃) 6
SQLインジェクション攻撃 7
カスタマイズした画面にSQLインジェクション(正常系) この画面はデモ 用に作成したも のです 8
SQLインジェクションで情報を盗む方法 • UNION SELECTを使う • エラーメッセージを使う • なんらかの1ビットの情報を積み重ねる(Blind SQLi) – – – – – 表示上のなんらかの差異 エラーの有無 ステータスコードの違い(200 or 500など) 更新できるか否か 時間差 (Time-base SQL Injection) 9
information_schema 10
カスタマイズした画面にSQLインジェクション(検証例) 続きはデモで $state = $_POST['state']; $city = $_POST['city']; $town = $_POST['town']; $db = mysql_connect(DB_SERVER, DB_USER, DB_PASSWORD); mysql_select_db(DB_NAME, $db); $r = mysql_query("SELECT * FROM mtb_zip WHERE state = '$state' AND city LIKE '$city%' AND town LIKE '$town%' LIMIT 100"); 11
日本で一番売れている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 文なので表示はない たにぐちまこと、「よく分かるPHPの教科書」 P272より引用 12
エスケープしているのになぜ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が削除される 13
エラーメッセージから情報窃取
• MySQLはエラーメッセージの中にリテラルの情報を含めな
いものが多いが、例外としてextractvalue関数がある
mysql> select extractvalue('<a><b>xss</b></a>', '/a/b');
+-------------------------------------------+
| extractvalue('<a><b>xss</b></a>', '/a/b') | ← 正常系
+-------------------------------------------+
| xss
|
+-------------------------------------------+
mysql> SELECT extractvalue('<a><b>xss</b></a>', '/$this is a pen');
ERROR 1105 (HY000): XPATH syntax error: '$this is a pen'
• 副問い合わせにより、extravalueにわざとエラーのあるクエ
リを用いて情報窃取
mysql> SELECT extractvalue('',concat('/$',(SELECT cardnumber FROM
eccube_db.dtb_customer_card LIMIT 1 OFFSET 0) ));
General error: 1105 XPATH syntax error: '$1234567890123456'
14
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インジェクション 15
SQLインジェクション対策はプレースホルダで • プレースホルダとは SELECT * FROM books WHERE id=? • 静的プレースホルダと動的プレースホルダ – 静的: サーバー側で値をバインドする(エスケープは必要 ない) – 動的: 呼び出し側で値をエスケープしてバインドする • 接続時に文字エンコーディングを指定する $db = new PDO('mysql:host=myhost;dbname=mydb;charset=utf8', DBUSER, DBPASS); • 列の型を意識する 16
サンプルコード(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();
17
クロスサイト・リクエストフォージェリ(CSRF) 18
横浜市のCSRF悪用の犯行予告手口(推測) http://d.hatena.ne.jp/Kango/20121008/1349660951 より許諾を得て引用 19
日本で一番売れている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の教科書」より引用
20
CSRFによる成りすまし投稿 ③被害者のブラウザ経由 で掲示板に書き込み 掲示板の書き込みログは 被害者のIPアドレス 21
CSRF対策の方法は? • • • • 色々な対策が知られている トークンによるもの 再認証 Refererのチェック • Captcha • … • 実際は、トークン一択と考えて良い 22
トークンはどうやって生成する? • ワンタイムトークンである必要はない – セッション毎に固定で良い – いわゆるワンタイムトークンだと処理が複雑になる • セッション毎に一度「安全な乱数生成器」で生成する – 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とセッションの両方に書いておいて、両 者を比較する。違っていたらエラー 23
クロスサイト・スクリプティング(XSS) 24
XSSはなぜ危険か? • XSSは、 – 利用者(被害者)のブラウザ上で – 攻撃対象のドメイン(オリジン)で – 攻撃者が自由にJavaScriptを実行できる • これって、ウイルス? – ウイルスではないが、結果としてウイルスと同じような被害 – XSSを悪用したウイルス(ワーム)はいくつかある • ブラウザを乗っ取られたのと同じ – 影響範囲はXSS脆弱性のあるページと同じドメイン(オリジン) – 同一オリジン上はすべてのページが影響を受ける ※オリジン=ホスト名+スキーム+ポート番号 25
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>
26
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から参照 27
クリックジャッキング 28
クリックジャッキング攻撃 • 機能を勝手に実行させられるという点でCSRFと類似 • ターゲットの画面をiframe上で「透明に」表示する • その下にダミーの画面を表示させる。ユーザにはダミーの画 面が透けて見える • 利用者がダミーの画面上のボタンを押すと、実際には全面の ターゲット画面のボタンが押される 本当の画面(前面、透明) ダミーの画面(後面) 29
CSRF/クリックジャッキングの対策
•
CSRF対策はトークンによる方法に統一しよう
–
•
「ワンタイムトークン」である必要はない
クリックジャッキングされると困るページには、X-FRAME-OPTIONSヘッダを指
定する(徳丸本P63)
– frame/iframeを禁止して良い場合
header('X-FRAME-OPTIONS', 'DENY');
– frame/iframeを禁止できないが単一ホストの場合
header('X-FRAME-OPTIONS', 'SAMEORIGIN');
•
CSRF対策のトークン発行しているページが対象となる
入力画面
メールアドレス
[email protected]
実行画面
メールアドレスアドレスを変更
しました
変更
header('X-FRAME-OPTIONS', 'DENY');
…
<input type="hidden" name="token"
value="a89daf89af0…">
セッション等に保存したtokeとhiddenの
token値を比較)
30
今時のウェブサイトの守り方 31
安全なWebサイトの構築方法 • ログインを守る – – ログインの入り口は外部に公開しない パスワード管理の徹底 • プラットフォームの脆弱性に対処する – – サイトのライフサイクル管理を計画する パッチ適用容易性を確保する • アプリケーションの脆弱性に対処する – – 脆弱性が「ないこと」をベンダーに要求 脆弱性検査 32
CMSやDBの管理コンソール(ログイン) • CMSやDBの管理コンソールが外部に公開されてい る状況は非常によろしくない • どうしても外部に公開しないと行けない場合は、以 下のいずれか、あるいは両方を実施 – IPアドレスを制限 – BASIC認証による制限 • アプリ側にログイン機能があるからと言って安心し ないこと – 保護が十分でない場合がある – アプリの脆弱性をつかれる場合がある 33
パスワード! パスワード! パスワード! • 極論するとユーザ名は"admin"でもよい – adminだと自動攻撃に狙われるのでウザいということはあ る • とにかくパスワードをちゃんとすることが重要 – – – – – – 8文字以上 英数字を混ぜる 辞書に載っている単独はだめ できればランダム文字列 他所で使ってないもの 【重要】 管理者が複数存在する場合は、管理者毎にユーザーを作 成する 34
ソフトウェアのバージョンアップまたはパッチ適用 • 脆弱性対処は、バージョンアップまたはパッチ適用 が基本 – 自らビルド等している場合はバージョンアップが楽な場 合が多い – CentOS、Debian、Ubuntu等のディストリビューション のパッケージを導入している場合はパッチ適用 • バージョンアップするとサイトが動かなくなる…な んて心配をしないで、とにかくバージョンアップす ること • 自力でトラブル対処ができないソフトは導入しない こと 35
どんなサーバーを借りたらよいか? セキュリティ対策 IaaS/VPS PaaS/レンサバ SaaS WAF 利用者 (事業者) - PHP/Apache 利用者 事業者 事業者 CMS 利用者 利用者 事業者 プラグイン 利用者 利用者 事業者 カスタマイズ部分 利用者 利用者 - パスワード 利用者 利用者 利用者 • IaaSやVPSは利用者が「全て」の対応をする必要がある • PaaSやレンタルサーバーはインフラの面倒は見てくれる • SaaSの場合、パスワードさえしっかり管理すれば 36
先の図は情報処理安全確保支援士試験に出題されました 平成29年度春期情報処理安全確保支援士試験 午前Ⅱ 問8 より引用 37
Webアプリケーションの有名な脆弱性 • SQLインジェクション • クロスサイト・スクリプティング(XSS) • クロスサイト・リクエスト・フォージェリ(CSRF) • … • 脆弱性の種類はたくさんあるが、せめて上記の三種 類は把握しておくこと • Webサイト公開前に脆弱性を修正しておく • カスタムアプリの場合、新規追加・修正がない限り、 頻繁に診断する必要性は薄い • セキュアプログラミングの体制を作る 38
まとめ • Webサイト攻撃のトレンドと対処法 • 攻撃の典型例 – SQLインジェクション攻撃 – CSRF – XSS – クリックジャッキング • 今時のウェブサイトの守り方 – 認証の強化、特にパスワード – 基盤ソフトウェアのパッチ適用 – セキュアプログラミングの体制を作る 39