Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
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インジェクション(検証例)
11
$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");
続きはデモで
日本で一番売れているPHP教科書のサンプル
12
// ここまでで、認証済みであるこの検査が済んでいる
$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より引用
エスケープしているのになぜ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悪用の犯行予告手口(推測)
19http://d.hatena.ne.jp/Kango/20121008/1349660951 より許諾を得て引用
日本で一番売れている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: }
20
CSRF脆弱性
対策していない
続きはデモで
たにぐちまこと著「よくわかるPHPの教科書」より引用
CSRFによる成りすまし投稿
21
③被害者のブラウザ経由
で掲示板に書き込み
掲示板の書き込みログは
被害者のIPアドレス
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対策済みの掲示板で、なりすまし書
き込みをやってみよう
• 実行するスクリプトは下記のもの
26
<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>
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対策のトークン発行しているページが対象となる
30
入力画面
メールアドレス foo@pexample.jp
変更
実行画面
メールアドレスアドレスを変更
しました
header('X-FRAME-OPTIONS', 'DENY');
…
<input type="hidden" name="token"
value="a89daf89af0…">
セッション等に保存したtokeとhiddenの
token値を比較)
今時のウェブサイトの守り方
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 利用者 利用者 事業者
プラグイン 利用者 利用者 事業者
カスタマイズ部分 利用者 利用者 -
パスワード 利用者 利用者 利用者
36
• IaaSやVPSは利用者が「全て」の対応をする必要がある
• PaaSやレンタルサーバーはインフラの面倒は見てくれる
• SaaSの場合、パスワードさえしっかり管理すれば
先の図は情報処理安全確保支援士試験に出題されました
37
平成29年度春期情報処理安全確保支援士試験 午前Ⅱ 問8 より引用
Webアプリケーションの有名な脆弱性
• SQLインジェクション
• クロスサイト・スクリプティング(XSS)
• クロスサイト・リクエスト・フォージェリ(CSRF)
• …
• 脆弱性の種類はたくさんあるが、せめて上記の三種
類は把握しておくこと
• Webサイト公開前に脆弱性を修正しておく
• カスタムアプリの場合、新規追加・修正がない限り、
頻繁に診断する必要性は薄い
• セキュアプログラミングの体制を作る
38
まとめ
• Webサイト攻撃のトレンドと対処法
• 攻撃の典型例
– SQLインジェクション攻撃
– CSRF
– XSS
– クリックジャッキング
• 今時のウェブサイトの守り方
– 認証の強化、特にパスワード
– 基盤ソフトウェアのパッチ適用
– セキュアプログラミングの体制を作る
39

More Related Content

ウェブセキュリティの常識