Menu

SQLの乗っ取り対策をする

2015年10月25日20:49  投稿者 : PINION

フォームに入力してもらった文字列を、そのままデータベースに登録したり、その文字列でデータベース内を検索したりする事ってありますよね。
前者だと、会員制サイトでユーザー自ら自由な文字列でIDを登録する形式だったり、何かのコメント欄をそのままデータベースに保存したりするケースもるでしょう。
後者だと、ログインフォームなんかもそれにあたります。入力されたIDを検索するSQLを発行して、登録されていたらパスワードを照合して…という具合に。

しかし、「入力された文字列で、そのままSQLを発行する」というのは、SQLの乗っ取りが行われる事があり大変危険です。
たとえば、ログインフォームにIDとPWを入力してログインする時に

$uid = $_POST['id'];
$upw = $_POST['pw'];
$sql = "SELECT * FROM user WHERE uid='$uid' AND pwd='$pwd'";

なんて形で書いてしまうと、もしログインフォームのパスワード欄に「' OR 'PW'='PW」と入力されてしまうとどうでしょう?
実際のSQL文は

SELECT * FROM user WHERE uid='ID' AND pwd='' OR 'PW'='PW'

となってしまい、ログイン出来てしまいますね。
まあ、実際はパスワードをそのまま平文でデータベースに入れているケースは希でしょうし、一旦ハッシュ化するような処理をしていれば、これでログイン出来てしまう事はないでしょうが…
しかし、IDは入力されたまま照合したりしていると、そこで何らかのSQLを仕込まれる可能性はあります。

また、コメント欄なんかだと英文を書かれた場合など、文中にシングルクォーテーションが含まれる事なんか普通にあります(海外の方だと、お名前に'が入っている事もあります)から、そのまま扱おうとするとシングルクォーテーションの部分で切られて、その後の文はクエリーとして扱われてしまうため、エラーになってしまいますので、乗っ取りだけでなく、シングルクォーテーションがそのまま通らないようにする事は必要になります。

さて、PHPでもSQLでも¥を使ってエスケープする(¥'とする)事で、シングルクォーテーションを囲み文字ではなく文字列として扱う事が出来ます。
しかし、逆に¥を入力されるとどうなるでしょうか?
そう、¥直後のシングルクォーテーションがエスケープされて、「括りが終了していない状態」になります。
ここでも、意図しないクエリーエラーが発生したり、意図しないクエリーを実行されたりする事になります。
なので、単純にシングルクォーテーションをエスケープすれば良い訳ではない上に、エスケープ文字そのものを入力された場合の対応もしなくてはいけません。

簡単なのは、エスケープ文字である¥を¥¥などに置換した上で、htmlspecialcharsでENT_QUOTESを設定してシングルクォーテーションをエスケープする方法ですが、SQLの乗っ取りは上記の2つだけではないので、不完全です。

※ コメント欄などで、意図しないタグを仕込まれたり、悪意のあるJavaScriptを仕込まれたりしないように、htmlspecialcharsを使用する事は有効です。

クエリー文とパラメータを別々に渡す

では、どうすればいいのか?
それは、プレースホルダを使って、SQL文とパラメータを別々に渡す方法です。
mysqliでプレースホルダを使う場合
PDOでプレースホルダを使う場合

実際に発行されるSQL文は、各特殊文字をエスケープしたものになるようですが、自分のオリジナルでやると抜けが生じたりするので、この方法が安全でしょう。

$uid = $_POST['id'];
$upw = $_POST['pw'];

$sth = $pdo->prepare('SELECT * FROM user WHERE uid = ? AND pwd= ?');
$sth->execute(array($uid, $upw));
$result = $sth->fetchAll;

ただし、データベースに保存する時にプレースホルダを使用していても、クエリーを実行する時にエスケープされるだけで、保存された文字列はエスケープされていませんので、取り出した値を用いて再びクエリーを発行する場合は、その時にまたエスケープしないと同じ事が起こりますので、気を付けましょう。

コメントを残す

メールアドレスが公開されることはありません。

コメントフィード

トラックバックURL : http://www.mp-create.com/104.html/trackback

PHP & MySQLトップ