SQLインジェクションは本当に避けられないのか
ドクジリアン柔術少女 すから☆ぱいそん 2014年11月26日ありもしない完全な代替品を求めるよりも、より現実的な選択肢を改善することについて。
SQLからなるべく乖離しないでDRYを実現するっていうScalikeJDBCの落としどころが素晴らしい。「結局のところSQLは書かなきゃいけんねん」「SQLよりつぶしの効くRDB操作用言語は存在しえないねん」っていうORMの教訓を経てきた人類はここに到達したって感じで。
— 嶋田大貴 (@shimariso) 2014, 11月 21
というツイートをしたところ、
- 何をいうか。必ずORMを使うべきだ
- SQLは根本的にSQLインジェクションを回避できない問題がある
みたいな趣旨の反応があったのだけれど、前者についてはWikipediaのここ を一読いただくとして、後者についてはプログラミング言語のほうが発達してて状況が違ってきてるよという話をしたい。
誤解しないでいただきたいのは、別にSQLが良いものであるとは言ってないことだ。良くないものであっても現実的な話としてそれしか選択肢がないのであればそれと向き合うための工夫が必要なのであって、それに真面目に取り組むことは素晴らしいのだということである。
ScalikeJDBCのSQL型
ScalikeJDBCを使う場合において、SQLはそれがソースに記述された時点で文字列ではなく SQL型のオブジェクトとして扱われる。
sql"select * from emp where name="
上記の記述はいっけん文字列に見えるが、先頭に付いているsql がこれを文字列と異なる SQL型のオブジェクトであることを示している。型が違うので文字列とは連結できないし、文字列との相互変換もできない。例えば下記の記述はコンパイル時点でエラーになる。
val name = "scott"
sql"select * from emp where name='" + name + "'"
大事なことなのでもう一度。上記のコードは コンパイル時点でエラーになる。正しくは
val name = "scott"
sql"select * from emp where name=${name}"
のようにSQL型のオブジェクトに所定の書式で直接パラメータを埋め込む。SQL型のオブジェクトに埋め込まれたパラメータはScalikeJDBC内部で実行時にエスケープされるため、そこにどんなアブナイ入力値が潜んでいようともSQLインジェクションは起こらない。逆に言えば、SQLインジェクションが起こるのはアブナイ入力値を文字列連結でSQLに埋め込むからだ(そしてHQLやJPQLのようなORMの独自クエリ言語だって同じ問題を抱えている)。ならSQLと文字列の連結をそもそも不可能にしてしまえばいいというわけである。
(SQL文字列にプレースホルダを埋め込む典型的な PreparedStatement方式とどう違うのかと思われる方は、「コンパイルの時点で」間違いを検出出来るかどうかの違いについて考えてみてほしい)
SQLインジェクションは必ずしも不可避ではない
原因がミスにせよ知識不足にせよSQLインジェクションが起こる可能性のあるコードがコンパイラに弾かれさえすれば、それがサービスインして問題を起こすに至ることも当然ありえないことになる。旧来のプログラミング言語ではSQLを"文字列"として処理するしか方法がなかったため SQLインジェクションを起こしうる操作を禁止することは確かにできなかったが、言語が進化し物事に"文字列"やら"数値"やらといったプリミティブよりも複雑な意味をより静的に与えられるようになった今では、「根本的にSQLインジェクションは不可避である」とは言えないことになる。
(もっとも、常に最新のプログラミング言語を採用してモノを作れるわけではないという問題は別にある)
同じカテゴリの記事
カテゴリ ドクジリアン柔術少女 すから☆ぱいそん の記事一覧自分用のCMSを作ってみた その2 2014年11月25日
自分用のCMSを作ってみた 2014年11月24日
考えたことは絵に描いて誰かに伝えると良いんじゃ 2014年11月9日
2014年3月29日(土)の業務日報 2014年3月29日
お勧めカテゴリ
なじみ深い日本製アニメの英語版DVDで、字幕と音声から英語を学びましょうという趣旨のシリーズ記事です。
Scalaと Spring Frameworkを使って REST的なJSON APIを実装してみましょう。
代表 嶋田大貴のブログです。写真は神仏に見せ金をはたらく罰当たりの図