【社内勉強会】iBatisの`$`と`#`(2017/03/15)

# 0. はじめに

-------------------
## 前提知識
* iBatis2の基本的な使い方
* Javaの構文

-------------------
## 伝えたいこと

* iBatisの`$`と`#`の違い
* プリペアドステートメント
* SQLインジェクション


-------------------
## 目次
1. iBatisの概要
2. JDBCでデータベースにアクセス
3. iBatisの`#`と`$`
4. 付録


-------------------
# 1. iBatisの概要

-------------------
## iBatisとは?
O/Rマッピングツールの1つ。
Javaからデータベースに簡単にアクセスできる。
初版は2001年。

-------------------
### iBatisのサンプルコード

SQL文をXMLファイルに定義して、Javaから分離。
SELECT文の結果が、`resultClass`で指定したクラスのオブジェクトに格納される。
`#value#`はJavaで指定されたパラメータ。

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

```java
Product resultProduct = sqlMapClient.queryForObject("getProduct", 123);
```

[Wikipedia](https://ja.wikipedia.org/wiki/IBATIS) より引用


-------------------
## O/Rマッピングとは?
Object / Relational Mappingの略称。
オブジェクト指向言語(Javaなど)とリレーショナルデータベースの橋渡しとなるツール。

* オブジェクト指向設計: データモデルを現実世界のモデルに即したものとして定義
* リレーショナルデータベース設計: リレーショナルデータベースの検索や登録更新処理に最適なモデルを定義

設計思想の違いによる「インピーダンスミスマッチ」を解消するために生まれた。

[O/Rマッピングの役割とメリット](http://www.atmarkit.co.jp/ait/articles/0404/13/news075.html) より引用

-------------------
## JDBCとは?
Java Database Connectivityの略。
Java とリレーショナルデータベースの接続のためのAPI(`java.sql`, `javax.sql`パッケージ)。
iBatisの下位層に該当する。

-------------------
## Javaからデータベースにアクセスする順番

1. Plain Java(私たちが記述するコード) 
2. iBatis
3. JDBC
4. データベース

![iBatisの構成図.png](https://qiita-image-store.s3.amazonaws.com/0/37903/6fbc220c-9096-e98f-b821-8bedd4827ab3.png)


-------------------
# 2. JDBCでデータベースにアクセス

-------------------
## 環境構築

https://github.com/yuji38kwmt/learning


* [データベース作成SQL](https://github.com/yuji38kwmt/learning/blob/master/sql/%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E4%BD%9C%E6%88%90.sql)

* [Main Java](https://github.com/yuji38kwmt/learning/blob/master/src/learing/ibatis/MainIbatis.java)


-------------------
## [TRY]JDBCで単純なSQL文を実行しよう

```java
String sql = "SELECT id, name, population FROM area " +
				"WHERE name = '" + areaName +  "'";

Statement statement = connection.createStatement();
//SQLを実行
ResultSet rs = statement.executeQuery(sql);
//SELECT結果を１行ごとに出力
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`にいろんな値を代入してみよう

```java
String areaName = "' OR TRUE --";
String sql = "SELECT id, name, population FROM area " +
				"WHERE name = '" + areaName +  "'";
```

* `'OR TRUE`でWHERE句の条件を無効
* `--`で、末尾のシングルクォートを無効

実行すると、全行の結果が取得できる。
もしこのテーブルがユーザテーブルなら、個人情報漏洩につながる！！
SQLインジェクションに対する脆弱性あり！！

-------------------
## SQLインジェクション

>アプリケーションが想定しないSQL文を実行させることにより、データベースシステムを不正に操作する攻撃方法のこと。

[Wikipedia](https://ja.wikipedia.org/wiki/SQL%E3%82%A4%E3%83%B3%E3%82%B8%E3%82%A7%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3) より引用

-------------------
### SQLインジェクションの対策方法

1. 入力値チェックの徹底
2. 特殊記号のエスケープ
3. プリペアドステートメントの使用

[SQL注入](https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/502.html) より引用

1と2は漏れが発生する可能性があるので、3がベスト。


-------------------
## プリペアドステートメントとは？

>プリペアドステートメントは、プレースホルダと値埋め込みAPIから成る解析済みのSQL文であり、コード中のプレースホルダ(予約場所）に入力データを割り当てる機能(バインドメカニズム)である。

[SQL注入](https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/502.html) より引用


-------------------
### [Try] プリペアドステートメントでSQLを実行しよう

```java
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インジェクションを実行してみよう

```java
String areaName = "' OR TRUE --";
```

実行されたであろうSQL文(予想)

```sql
SELECT id, name, population FROM area 
WHERE name = ''' OR TRUE --'
```

取得結果は０件。

-------------------
# 3. iBatisの'$'と'#'

-------------------
## iBatisでパラメータをSQLに渡す方法

`#`または`$`でパラメータを渡せる

```xml
<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](https://ibatis.apache.org/docs/java/pdf/iBATIS-SqlMaps-2_ja.pdf) を参考


-------------------
## iBatisの`$`を使ってもよいとき

* IN句で大量のパラメータ(1000個以上)を渡すとき。多すぎるとエラーになる。（何個からエラーかは不明）
* テーブル名をパラメータとして渡したい

```xml

SELECT * FROM #tablename# <!-- エラー -->
SELECT * FROM $tablename$ <!-- OK -->
```

-------------------
# 4.付録

-------------------
# 参考サイト
[TECHSCORE](http://www.techscore.com/tech/Java/JavaEE/JDBC/index/)

[O/Rマッピングツールに対する誤解をときたい](http://blog.jnito.com/entry/20101228/1293491597)

[O/Rマッパーによるトラブルを未然に防ぐ](https://www.slideshare.net/kwatch/how-topreventorm-troubles)