SQLインジェクション入門:仕組みと対策
列の数を数える
この回でやること
UNIONを成り立たせるには元の問い合わせの列数を知る必要があります。ORDER BYやNULLでエラーの境目を探し、総当たりでなく二分探索的に絞る考え方を身につけます。
- 読む 約 8 分
- 最後にクイズ 1 問
このレッスンで学ぶこと
列の数は、元の問い合わせが何列返すか分からなくても、エラーの出る境目から数えられます。この回は ORDER BY や NULL で境目を探し、なぜ総当たりでなく半分ずつ絞れるのかを、同じ users テーブルで見ます。
読み終えると、20列あっても数回で当てられる絞り方を、境目の並びから説明できるようになります。
ORDER BYで境目を探す
ORDER BY 2 は「2番目の列で並べ替えろ」という意味です。存在する列を指せば通り、無い番号を指すとデータベースはエラーを返します。ここを利用します。番号を1つずつ上げて、通る番号と落ちる番号の境目を見つければ、それが列の数です。
SQL クエリ
SELECT name, role FROM users WHERE name = 'tanaka' ORDER BY 3 --'この元の問い合わせは2列なので、ORDER BY 2 までは通り、ORDER BY 3 で落ちます。境目は2と3の間、つまり列数は2です。ORDER BYを使う利点は、UNIONと違って型を揃えなくてよいところにあります。数だけを純粋に測れます。
NULLで数える手もある
もう一つ、UNIONに直接NULLを並べる手があります。NULL はどんな型の列にも収まるので、型の一致を気にせず数だけを合わせにいけます。
SQL クエリ
SELECT name, role FROM users WHERE name = 'tanaka' UNION SELECT NULL, NULL --'NULLの数が元の列数と一致したときだけ、文が成り立って通ります。1つずつ増やし、通った時点のNULLの数が列数です。
総当たりでなく半分ずつ
番号が列数以下なら必ず通り、超えれば必ず落ちる。この並びは一方向で、途中で入れ替わりません。だから1から1つずつ試さなくても、大きく跳んで範囲を半分ずつ詰められます。
たとえばまず ORDER BY 8 を試します。落ちれば列数は8より下だと分かり、範囲は1から7に縮みます。次に真ん中の ORDER BY 4 を試し、通れば4以上7以下、落ちれば1以上3以下へ。こうして毎回半分に割っていくと、20列あっても数回で当たります。1、2、3と順に上げる総当たりは、列数が多いほど手数がかさみますが、半分ずつなら手数はゆるやかにしか増えません。
だから手数を減らす意味があります。試行が少ないほど記録に残りにくく、跳んで絞るのは速さだけでなく足跡を減らす工夫でもあります。もっとも、これは自分の環境で仕組みを確かめるための考え方です。
直す方向と試し方
列を数えられるのは、そもそも入力がSQLの構文へ食い込むからで、値を命令から切り離して渡せば ORDER BY も UNION も文字列の一部で終わります。手を動かすなら、自分で用意した練習用のデータベースで番号やNULLの数を変え、通る境目とエラー文を読みます。この章の演習はログインの穴が題材でUNIONやORDER BYは扱いません。試すのは自分が権限を持つ練習用の環境だけにします。