今更EOLしたS2Daoについてです。
業務で利用しているのですが、使い方を誤ると危険なのでメモしておきます。
埋め込み変数
S2Daoでは埋め込み変数というものが用意されています。
こちら使い方を誤ると大変危険な挙動になるのでメモとして残しておきます。
埋め込み変数の使い方
埋め込み変数は以下のように記述します。
/*$引数名*/リテラル
実際のコード
BalanceDao
@S2Dao(bean = Balance.class)
public interface BalanceDao {
@Sql("select id, name, amount, created_at, updated_at from balance where name = '/*$name*/'田中''")
public List<Balance> findByName(String name);
}
動かしてみます
curl
$ curl -G "localhost:8080/sastruts/balance/findname" --data-urlencode "name=田中太郎"
id:1name:田中太郎created_ad:2019-05-03 19:25:33.0updated_ad:null
実行時ログ
DEBUG 2019-05-04 12:51:36,087 [http-nio-8080-exec-5] select id, name, amount, created_at, updated_at from balance where name = '田中太郎'
このように /$name/ で設定した部分(埋め込み変数)が置き換えられてSQLが実行されることがわかります。
SQLインジェクション
では埋め込み変数の何が危険なのかというと、SQL部分が引数の内容でそのまま置き換えられてしまうため、SQLインジェクションの危険があるということです。
試してみます。
SQLインジェクションしてみる
curl
$ curl -G "localhost:8080/sastruts/balance/findname" --data-urlencode "name=A'or'A'='A"
id:1name:田中太郎created_ad:2019-05-03 19:25:33.0updated_ad:null
id:2name:佐藤花子created_ad:2019-05-03 23:48:40.0updated_ad:null
実行時ログ
DEBUG 2019-05-04 12:54:57,636 [http-nio-8080-exec-6] select id, name, amount, created_at, updated_at from balance where name = 'A'or'A'='A'
このように全てのデータが取得できてしまいます。
対策
2つ対応方法があると思っています。
- バインド変数コメントを利用する
- Daoの呼び出し側でSQLインジェクション対策を行う
基本的にはバインド変数コメントを利用する方が良いと思います。
使い方は簡単です。
以下の指定をするだけです。
/*引数名*/リテラル
バインド変数の使用例
BalanceDao
@Sql("select id, name, amount, created_at, updated_at from balance where name = /*name*/'田中'")
public List<Balance> findByName(String name);
SQLインジェクションを試してみます。
何も返却されない
$ curl -G "localhost:8080/sastruts/balance/findname" --data-urlencode "name=A'or'A'='A"
$
実行時ログ
DEBUG 2019-05-04 13:01:31,270 [http-nio-8080-exec-3] select id, name, amount, created_at, updated_at from balance where name = 'A'or'A'='A'
実行ログ上ではSQLインジェクションに成功しているように見えますが、実際には何も返却されませんでした。