INSERT文にSQLインジェクション脆弱性があるとどんな被害が出るのか?

という徳丸さんのツイートがありましたので、ちょっと考えてみました。

サンプルコード

上記の質問にあるコードを動作するように最低限補完しました。

<form method="post">
<th>ご住所</th>
<td><input type='text' name='address'></td>
<th>メールアドレス</th>
<td><input type='text' name='mail'></td>
<input type='submit' value='送信'>
</form>

--------------------

<?php
if (isset($_POST)) {
    $address = $_POST['address'];
    $mail = $_POST['mail'];

    $con = mysql_connect('localhost','root','root');
    mysql_select_db("php_dev", $con);
    $result = mysql_query("INSERT INTO tbl(address, mail) VALUES('$address', '$mail')", $con);
}
?>

<pre>
<?php
    echo "INSERT INTO tbl(address, mail) VALUES('$address', '$mail')";
?>
</pre>

攻撃方法

徳丸さんにより、

  • VALUES句に複数の行を指定
  • ON DUPLICATE KEY UPDATEの悪用によるUPDATE

が紹介されていますが、どちらもインパクトが弱いです。

ということで、もう少しインパクトのあるサブクエリを使う方法を検討してみました。

住所:test', (select password from mysql.user where user='root' limit 0,1)) -- -
メールアドレス:dummy@example.jp

これで、実行されるSQL文は以下になり、mailコラムにMySQLのrootユーザのパスワードハッシュが挿入されました。

INSERT INTO tbl(address, mail) VALUES('test', (select password from mysql.user where user='root' limit 0,1)) -- -', 'dummy@example.jp')

あとは、自分のメールアドレスを表示するページで挿入した情報を表示させればOKというシナリオです。

言うまでもなく、サブクエリは自由に記述できるので、アクセスできるすべてのテーブルの任意の(挿入可能な型の)データを挿入させることができます。作業を繰り返せば、例えば、Webアプリの登録ユーザのID/パスワードをすべて抜くなどということも可能でしょう。

ということで、INSERT文のSQLインジェクションといえども現実的にはかなり深刻な被害が出る可能性があります。

まとめ

結論としては、徳丸さんの回答にあるように

「SQLインジェクション脆弱性はあるが攻撃はできそうもない」というケースもあり得ます。あり得ますが、思わぬ攻撃を受ける可能性もあり、その場合の被害は甚大ですので、攻撃可能性の有無に関わらず、必ずSQLインジェクション対策はしておくべきです。

となります。

実際、攻撃というのはいろいろなパターンの組み合わせであり、自分がうまく攻撃方法を想像できないからと言って本当に攻撃できないかどうかは全く分かりません。

このINSERTのSQLインジェクションで、データがUPDATEされる可能性やデータが抜かれる可能性を想像できなかった人は、確実に自分の知らないパターンがあると思った方がいいでしょうね。

また、MySQL関数で複文が使えないことからか、PHPでのMySQLでは複文による攻撃は不可能と思われていた時期もあり、私も以前はPDO+MySQLでも複文は使えないと誤解していました。

このように攻撃方法を考えるのはいろいろな知識や発想が要求され難しい場合も多いのですが、逆に防御についてはかなりパターン化されており、SQLインジェクションなどはとくに完全にパターン化しているのであらゆる場面で原則どおりに対応しておけばOKです。

SQLインジェクション対策については、以下の記事を参照してください。

参考

Date: 2015/01/06

Tags: sql, security