SQLインジェクション(弊研究室の某課題について考える18日目)

はじめに

失踪はしない

弊研究室の某課題について考える18日目の記事です.

SQLインジェクションの簡単な説明です。またこの記事はこのような攻撃手法があるので注意しましょうという目的であり、攻撃を推奨するものではありません。

SQLインジェクション(SQLi)とは

SQLインジェクション(英: SQL Injection)とは、アプリケーションのセキュリティ上の不備を意図的に利用し、アプリケーションが想定しないSQL文を実行させることにより、データベースシステムを不正に操作する攻撃方法のこと。また、その攻撃を可能とする脆弱性のことである。

例えば下のようなphpファイルでsqlにアクセスすることを考えます。(わざと悪い方法を使って書いています)

<?
if(isset($_POST["send"])){
    $id = $_POST["id"];
    //データベース処理
    $dsn = sprintf('mysql: host=%s; dbname=%s; charset=utf8',$db['host'],$db['dbname']);
    try{
        $pdo = new PDO($dsn,$db['user'],$db['pass'],array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
        $query = 'SELECT * FROM list WHERE id = '.$id;
        $stmt = $pdo->prepare($query);
        $stmt->execute();
    }catch(PDOException $e){
        $errorMessage = "データベースエラー";
        echo $e->getMessage();
    }
}
?>

HTMLはこんな感じ

<form method="post">
<label for="id">番号</label><input type="text" id="id" name="id" placeholder="数字を入力してください">
<input type="submit" id="send" name="send" value="送信">
</form>

さて$query = 'SELECT * FROM list WHERE id = '.$id;の部分に脆弱性があります。$idがそのまま文字連結されていますが、この$idはユーザの入力によってどのような入力であってもよい形になっています。

この$id1 or 1=1と入力すると$query = 'SELECT * FROM list WHERE id = 1 or 1=1'となります。するとwhereの中のid = 1 or 1=1trueなのでlistテーブルの全出力が得ることができます。

また例えば$query = 'SELECT * FROM list WHERE id = '.$id.' and passwd = '.$passwdといったqueryがあった際にid1 or 1=1 --とするとクエリが$query = 'SELECT * FROM list WHERE id = 1 or 1 = 1; -- and passwd = '.$passwdとなります.

--sqlコメントアウトなのでpasswdの値がなんであってもこのsqlは正しく通ります。

ブラインドSQLインジェクション

SQLインジェクションを利用すれば様々なクエリを動かすことが可能です。ですがテーブルの情報を消したり更新したりするにはテーブルのカラム名等を知る必要があります。そういうときに使う攻撃方法がブラインドSQLインジェクションになります。

ブラインドSQLインジェクションとはSQLインジェクション脆弱性は存在するがSQLの結果やエラー表示等が見れないときにでも成立する攻撃方法です。SQLインジェクションは存在するのでクエリの実行判定で確認します。

クエリの実行結果が確認できる場合は単純です。一文字ずつ確認していきます。

上の例においてid1 and substr(pass,1,1) = "A" --とすることでpassの一文字目がAであれば実行できます。アルファベット順で試すと時間がかかるので2分木探索をします。

1 and substr(pass,1,1) > "N" --とかこんな感じで定めていくことで効率よくやります。

ではクエリの実行結果が確認できない場合はどうするかというとsleep関数を用います。

クエリが正しく実行された場合はsleep関数によって定められた時間とまるはずなので止まった時間を用いてクエリを判定します。

参考

「ブラインドSQLインジェクションとは何ですか?」への回答 - 徳丸浩のtumblr