前回、SOY2DAOクラスで、SQL構文をそのまま実行して、
一つの配列に結果がすべて入ったものを返すという内容を記載しました。
SQL構文をそのまま実行できることが分かれば、
次に知りたいのはプリペアードステートメントだよね。
プリペアードステートメントといえば、
例えば名前で検索するフォームを設置して、
入力した名前を元にSQL構文を作成して実行する時に用いられるもので、
これをしないと、SQLインジェクションという攻撃を受けて大変なことになってしまう。
SQLインジェクションというのは、
例えば、下記の様にメールアドレスとパスワードを入力してもらってログインをするためのSQLを書いたとする。
$sql = "SELECT * FROM soyshop_user WHERE mail_address = '" . $m . "' AND password = '" . $p . "'";
このコードであれば、メールアドレスとパスワードが一致していなければ、
該当するユーザの配列を得ることは出来ないんだけど、
例えば入力の際に、
//下記の二行はフォームから入力した値だと思ってください。 $m = "info@example.com"; $p = "' OR password LIKE '%%";
という値を入れたとすると、
構築されるSQL構文は、
SELECT * FROM soyshop_user WHERE mail_address = 'info@example.com' AND password = '' OR password LIKE '%%'
になって、末尾にパスワードだったら何でも良いよ(password LIKE '%%')という曲者のSQLが入りこんでしまっている。
これだと、このSQL構文が書かれているログインシステムであれば、
パスワードに何を入れても、メールアドレスの正しさなんて関係なくログインできるようになってしまう。
※OR password LIKE '%%'が入っているせいで、他のWHERE節のどの式も無効になっている
これを避けるために利用するのがプリペアードステートメントだ。
SOY2DAOでプリペアードステートメントをどのように実装するか?なんだけど、
$sql = "SELECT * FROM soyshop_user WHERE mail_address = :mail AND password = :pass";
この様にフォームで入力された値を許可するカラムの値のところを:(コロン)から始まる文字列にしておいて、
$dao = new SOY2DAO(); $binds = array(":mail" => $m, ":pass" => $p); try { $results = $dao->executeQuery($sql, $binds); } catch(Exception $e) { $results = array(); }
SOY2DAOのexecuteQueryメソッドの第二引数に各値の配列を指定して実行することで、
各値の中にORだとかカラム名だとかといったSQLインジェクションの可能性がありそうなものを調べて実行してくれる。
これでPOSTの値にORが入ってることを気にしなくて良くなった。
追記
今回はパスワードの話を書いたけど、実際にはパスワードでは今回のようなことは発生しない。
なぜならば、入力したパスワードはSQLを実行する前に文字列が一方向ハッシュ関数でハッシュ化されるから。
Goで一方向ハッシュ関数によるパスワードの暗号化を書いてみた
今回のSQLでSQLインジェクションをするなら、メールアドレスの方だね。