0. はじめに
前提知識
- iBatis2の基本的な使い方
- Javaの構文
伝えたいこと
- iBatisの$と#の違い
- プリペアドステートメント
- SQLインジェクション
目次
- iBatisの概要
- JDBCでデータベースにアクセス
- iBatisの#と$
- 付録
1. iBatisの概要
iBatisとは?
O/Rマッピングツールの1つ。
Javaからデータベースに簡単にアクセスできる。
初版は2001年。
iBatisのサンプルコード
SQL文をXMLファイルに定義して、Javaから分離。
SELECT文の結果が、resultClassで指定したクラスのオブジェクトに格納される。
#value#はJavaで指定されたパラメータ。
<select id="getProduct"
	parameterClass="java.lang.Long"
	resultClass="com.example.Product">
		select
			PRD_ID		as id,
			PRD_DESCRIPTION	as description
		from 
			PRODUCT
		where 
			PRD_ID = #value#
</select>
Product resultProduct = sqlMapClient.queryForObject("getProduct", 123);
Wikipedia より引用
O/Rマッピングとは?
Object / Relational Mappingの略称。
オブジェクト指向言語(Javaなど)とリレーショナルデータベースの橋渡しとなるツール。
- オブジェクト指向設計: データモデルを現実世界のモデルに即したものとして定義
- リレーショナルデータベース設計: リレーショナルデータベースの検索や登録更新処理に最適なモデルを定義
設計思想の違いによる「インピーダンスミスマッチ」を解消するために生まれた。
O/Rマッピングの役割とメリット より引用
JDBCとは?
Java Database Connectivityの略。
Java とリレーショナルデータベースの接続のためのAPI(java.sql, javax.sqlパッケージ)。
iBatisの下位層に該当する。
Javaからデータベースにアクセスする順番
- Plain Java(私たちが記述するコード)
- iBatis
- JDBC
- データベース
2. JDBCでデータベースにアクセス
環境構築
[TRY]JDBCで単純なSQL文を実行しよう
String sql = "SELECT id, name, population FROM area " +
				"WHERE name = '" + areaName +  "'";
Statement statement = connection.createStatement();
//SQLを実行
ResultSet rs = statement.executeQuery(sql);
//SELECT結果を1行ごとに出力
while (rs.next()) {
	int id = rs.getInt("id");
	String name  = rs.getString("name");
	int population = rs.getInt("population");
    System.out.println(id + "," + name + "," + population);
}
iBatisと比較したJDBCの面倒なこと
- SQL文をJavaで定義していて、文字列処理が必要。
- SELECT結果の取り出し処理が必要。
この面倒なことを省けるのが、O/Rマッピングツールのメリット。
[TRY] 変数areaNameにいろんな値を代入してみよう
String areaName = "' OR TRUE --";
String sql = "SELECT id, name, population FROM area " +
				"WHERE name = '" + areaName +  "'";
- 
'OR TRUEでWHERE句の条件を無効
- 
--で、末尾のシングルクォートを無効
実行すると、全行の結果が取得できる。
もしこのテーブルがユーザテーブルなら、個人情報漏洩につながる!!
SQLインジェクションに対する脆弱性あり!!
SQLインジェクション
アプリケーションが想定しないSQL文を実行させることにより、データベースシステムを不正に操作する攻撃方法のこと。
Wikipedia より引用
SQLインジェクションの対策方法
- 入力値チェックの徹底
- 特殊記号のエスケープ
- プリペアドステートメントの使用
SQL注入 より引用
1と2は漏れが発生する可能性があるので、3がベスト。
プリペアドステートメントとは?
プリペアドステートメントは、プレースホルダと値埋め込みAPIから成る解析済みのSQL文であり、コード中のプレースホルダ(予約場所)に入力データを割り当てる機能(バインドメカニズム)である。
SQL注入 より引用
[Try] プリペアドステートメントでSQLを実行しよう
String sql = "SELECT id, name, population FROM area " +
				"WHERE name = ? "; //バインドしたいところに"?"を記述
PreparedStatement statement = connection.prepareStatement(sql);
//バインドする
statement.setString(1, areaName);
// SQL実行
ResultSet rs = statement.executeQuery();
[Try] プリペアドステートメントでSQLインジェクションを実行してみよう
String areaName = "' OR TRUE --";
実行されたであろうSQL文(予想)
SELECT id, name, population FROM area 
WHERE name = ''' OR TRUE --'
取得結果は0件。
3. iBatisの'$'と'#'
iBatisでパラメータをSQLに渡す方法
#または$でパラメータを渡せる
<select id="getSample1"
  parameterClass="java.lang.String"
  resultClass="java.util.HashMap">
  SELECT * FROM
  WHERE area = #value#
</select>
<select id="getSample2"
  parameterClass="java.lang.String"
  resultClass="java.util.HashMap">
  SELECT * FROM
  WHERE area = $value$
</select>
iBatisの$と#の違い
パラメータを渡す方法に違いがある。
- 
#: プリペアドステートメントでパラメータを渡す
- 
$: 文字列処理としてパラメータを渡す
SQLインジェクション対策として、特に理由がない限り#を使うべき。
iBatisのタグを駆使すれば、大抵のSQL文は#で対応できる。
iBatis2 を参考
iBatisの$を使ってもよいとき
- IN句で大量のパラメータ(1000個以上)を渡すとき。多すぎるとエラーになる。(何個からエラーかは不明)
- テーブル名をパラメータとして渡したい
SELECT * FROM #tablename# <!-- エラー -->
SELECT * FROM $tablename$ <!-- OK -->

