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 -->