SQLインジェクション入門:仕組みと対策
どんなテーブルと列があるかを調べる
この回でやること
列名を当てずっぽうで打つ前に、どのテーブルとどの列があるかを調べます。メタ情報のありかが製品ごとに違うという一点を押さえます。
- 読む 約 8 分
- 最後にクイズ 1 問
このレッスンで学ぶこと
表や列の名前は、当てずっぽうで打つ前に、データベース自身が持つメタ情報から調べられます。この回は構造そのものを読む手と、その情報が製品ごとに別の場所に置かれる事情を、同じ users テーブルで見ます。
読み終えると、名前を1つずつ当てるより先に構造を読むほうが、なぜ手数が少なく済むのかを説明できるようになります。
当てずっぽうは高くつく
password という列名も users という表名も、こちらの想像にすぎません。総当たりで users, members, accounts と表名を、password, passwd, pwd と列名を打ち続けるのは手数がかさみ、外せば毎回エラーが返るだけです。手を動かす回数が増えるほど記録にも残ります。構造を先に知ってから狙うほうが、はるかに少ない手数で済みます。
データベースは自分の構造も表として持つ
多くのデータベースは、どんな表と列があるかという情報自体を、また別の表として持っています。標準的な製品では information_schema にまとまっていて、information_schema.tables を読めば表の一覧が、information_schema.columns を読めば列の一覧が返ります。前回のUNIONの手で、この表の中身をそのまま運び出せます。
SQL クエリ
SELECT name, role FROM users
WHERE name = 'no-such-name'
UNION SELECT table_name, NULL
FROM information_schema.tables --'映る列に表名が並びます。users があると分かったら、次はその列を読みます。
SQL クエリ
SELECT name, role FROM users
WHERE name = 'no-such-name'
UNION SELECT column_name, NULL
FROM information_schema.columns --'name / password / role という列名が出てきます。ここまで来てはじめて、前回の password を狙う一手が「当てずっぽう」ではなく「調べた結果」になります。
ありかは製品ごとに違う
information_schema は、どの製品にもある共通の場所ではありません。MySQL や PostgreSQL は持っていますが、Oracle は all_tables や all_tab_columns、古い SQLite は sqlite_master という別の表に同じ情報を置きます。だから調べる前に、まず相手がどの製品かを見当づけます。エラー文の言い回しや、使える関数の違いが手掛かりになります。
列名を1つずつ当てるのは、外れるたびエラーが返る遠回りです。メタ情報の表を先に読めば、表と列の名前が一覧で手に入ります。攻撃が速いのは無闇に撃つからではなく、撃つ前に地図を見るからです。
攻撃のための表ではなく、道具のための表です。管理ツールやアプリが「この表にはどんな列があるか」を問い合わせ、画面や処理を組み立てます。正規の用途で要るから常にあり、入力がSQLへ食い込む穴があると、同じ表が攻撃側からも読めてしまいます。
直す方向と、この講座で試せる範囲
根は前回までと同じで、値を命令から切り離して渡せば information_schema と書いても文字列の一部で終わります。この講座の演習で扱うのはログインの通り抜けまでで、メタ情報の表を実際に読む口はありません。ここから先は考え方だけを押さえます。手を動かすなら、自分で用意した練習用のデータベースで、tables や columns を読んで返る一覧を見ます。試すのは自分が権限を持つ練習用の環境だけにします。