SQLインジェクションの対策をやってみる

セキュリティ

以前にSQLインジェクションの検証を行ったのですが、その際に対策を記載していなかったので、今回は対策について記事にしていきます。

なお、間違っている部分等があるかもしれませんが、気付いた方は優しく教えて頂けると非常に助かります。

対策方法

これは有名で一般的ですが、「プレースホルダ」を使用します。

プレースホルダによる組み立てとは、パラメータ部分を「?」などの記号で示しておき、後に、そこへ実際の値を機械的な処理で割り当てる方法です。
IPA安全なSQLの呼び出し方:https://www.ipa.go.jp/files/000017320.pdf

作成したSQLインジェクションの検証環境ですが、SQLの組み立て個所は以下のようになっています。

$mysqli = new mysqli('localhost', 'tokoroten', 'tokoroten', 'user');

$uid = $_POST['uid'];
$pass = $_POST['password'];

$sql= "SELECT * FROM users where uid = '$uid' AND passwd = '$pass'";

見ればわかりますが、アプリケーションからの入力値をそのまま使用してSQLを組み立ててますね。

これではSQLインジェクションが発生してしまいますね。

これをプレースホルダを用いて修正していきます。(念のため、元ファイルはバックアップしておきましょう。)

結構色々悩みましたが、以下のように修正を行いました。

<?php
$mysqli = new mysqli('localhost', 'tokoroten', 'tokoroten', 'user');

$uid = $_POST['uid'];
$pass = $_POST['password'];

$sql = $mysqli->prepare("SELECT * FROM users where uid = ? AND passwd = ?");
#プリペアドステートメントを作成、uidとpasswdのイコールの後を「?」にする(プレースホルダ)

$sql->bind_param("ss", $uid, $pass);
#パラメータをバインド、方はstringなので「s」を指定
$sql->execute();
#クエリの実行
$sql->bind_result($uid,$passwd,$mail);
#結果変数をバインド。色々取得してしまうので、仕方なく変数を3つ作成、クエリを変更すれば変数は$mailだけで済む
$sql->store_result();
#結果を保存、これがないとnum_rowsを取得できない。

if ($sql->num_rows == 0) {
echo "ユーザ名または、パスワードに誤りがあります。";
exit;
}

while ($sql->fetch()) {
#fetch()すると バインドした変数に値がセットされる
print('mail addressはこちらです '.$mail);
print('<br>');
}

$sql->close();
$mysqli->close();
?>

※HTMLの部分は変更ないので、PHPコードの部分のみ記載しています。

クエリの部分は「”SELECT mail FROM users where uid = ? AND passwd = ?”」とすれば「bind_result()」の部分は「$mail」だけで大丈夫です。

中々てこずってしまいましたね。

結果を取得して表示する箇所で、「fetch_assoc()」が使用できなかったので、色々考えていたら、時間を使ってしまいましたね。(ここら辺は普段書いていないせいですな・・・)

もっといい方法があれば教えて頂けると助かります。

本当に修正できたかの検証

手作業で調べてもいいのですが、以下で情報を取得したように、sqlmapに調べさせてみます。

sqlmapを使ってデータベースの中身をのぞいてみよう!

やり方は同じです。

まずはテキストにリクエストを記載して保存し、Kali Linux上からコマンドでテキストを指定して実施します。

$ sqlmap -r test.txt -p uid

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting at 00:23:58

(中略)

[00:24:00] [WARNING] POST parameter 'uid' does not seem to be injectable

途中はすっ飛ばしてますが、最後に「POST parameter ‘uid’ does not seem to be injectable」となっていますので、パラメータ「uid」にSQLインジェクションは無くなっていることがわかるかと思います。

SQLインジェクションが見つからないときは以下のようの画面になります。

同様に「passwd」も同じ結果になることを確認しています。

まとめ

SQLインジェクションの対策として、プレースホルダを使用した対策方法を記載しました。

普段プログラムを書かないので、結構時間がかかりましたね。(今回は書くって程でもないと思いますが。。。)

たまには頭を悩ませてこういったこともするのも楽しいなと思いました。

なお、プレースホルダがどうしても使用できない場合は値のチェックを徹底する必要があります。

受け付けるパラメータが一つや二つで型がちゃんと決まっていればいいですが、ミスが発生しやすい箇所になりますので、一般的にはプレースホルダを使用することを推奨します。

以下を参考に修正を行いましたので、参考にして頂ければと思います。

・参考:http://php.net/manual/ja/mysqli-stmt.bind-param.php

不明点や要望やこういったこともやって欲しいとの要望があれば、お問い合わせページやコメント、ツイッターからでも結構ですので、気軽にご連絡ください。

コメント