Do You PHP?    
Search Engine Optimization  php5 powerd  Valid XHTML 1.0!  Valid CSS!  このサイトのはてなブックマーク数 



last updated
2002/10/19

counter hits
since 1999/11/06


ダイレクトSQLコマンドインジェクションについて

caution間違いやご意見がありましたら、ご指摘下さい

クロスサイトスクリプティングについて」に続き、IPA/ISECネタ第2弾です。それにしても、「ダイレクトSQLコマンドインジェクション」って長いですね。。。(^-^;

ダイレクトSQLコマンドインジェクション とは

ダイレクトSQLコマンドインジェクション攻撃とは,引数などのパラメタにSQL文を混ぜ込んでおき(インジェクション),プログラム内部でそのSQL文を実行させてしまう攻撃手法(IPAセキュリティセンター(IPA/ISEC)のセキュア・プログラミング講座から引用)のこと。

詳細については、IPA/ISECの以下のページに分かりやすく書かれています。

イメージとしては、「SQL版クロスサイトスクリプティング」というところでしょうか?SQL文を作成する際、where句のパラメータを指定することが多いと思いますが、パラメータ自体にSQLを埋め込まれた場合、意図しないSQLになってしまいそれが実行されてしまう、というものです。

caution以下のサンプルは、DBとしてOracleを対象としています。

例えば、scottユーザーのempに含まれる給与カラムを更新するスクリプトは、大体以下のような感じになります。

●SQLIinjection01.php

<?php
    if (isset($_POST["sal"]) && isset($_POST["empno"])) {
        $sql = "UPDATE emp SET sal = " . $_POST["sal"] . " WHERE empno = " . $_POST["empno"];
        $conn = OCILogon("scott", "tiger", "orcl");
        $stmt = OCIParse($conn, $sql);
        @OCIExecute($stmt);
        if (!OCIError($stmt)) {
            OCICommit($conn);
            echo "commitしました<br>";
        }
        else {
            OCIRollback($conn);
            echo "rollbackしました<br>";
        }
    }

    $sql = "SELECT empno, sal FROM emp ORDER BY empno";
    $stmt = OCIParse($conn, $sql);
    OCIExecute($stmt);
    $ncols = OCINumCols($stmt);

    echo "<TABLE BORDER='1'>";
    echo "<TR>";
    for ( $i = 1; $i <= $ncols; $i++ ) {
        $column_name  = OCIColumnName($stmt,$i);
        echo "<TH>" . $column_name . "</TH>";
    }
    echo "</TR>";

    while(OCIFetch($stmt))
    {
        echo "<TR>";
        for ( $i = 1; $i <= $ncols; $i++ ) {
            $column_name  = OCIColumnName($stmt,$i);
            echo "<TD>" . OCIResult($stmt, $column_name) . "</TD>";
        }
        echo "</TR>";
    }

    echo "</TABLE>";

    OCIFreeStatement($stmt);
    OCILogoff($conn);
?>
<form action="SQLIinjection01.php" method="post">
従業員番号<input type="text" name="empno">の給与を<input type="text" name="sal">に更新します<br>
<input type="submit">
</form>

ここで、入力する値を

  • 従業員番号:「7369 or empno = 7499」
  • 給与:9999

とした場合、結果として作成されるUPDATE文は、

UPDATE emp SET sal = 9999 WHERE empno = 7369 or empno = 7499

となってしまいます。給与が増やされるのは良いんですが、減らされたら。。。(^-^; それはさておき、もしこれが「ユーザーのパスワード変更機能」だったら。。。かなり怖いですね。

PHPでの対策方法 その1(パラメータチェックを怠らない)

まずは、サーバーサイドでの入力文字チェックを行うことです。上記のサンプルのような、「入力されたかどうか」ではなく、特定の文字のみを受け付ける項目(ユーザーIDなど)や特定の文字パターンに従う項目(商品番号など)があるとき、ereg関数などの正規表現関数を使ってチェックしましょう。

こうすることで、入力された値にSQLの断片が埋め込まれた場合に対処できます。

●PHPでの入力値チェックの例

<?php
                          :
    if (!(isset($_POST["sal"]) && isset($_POST["empno"]))) {
        echo "従業員番号・給与を入力してください<br>";
    }
    else if (!(ereg("[0-9]{1,4}", $_POST["sal"]) &&
               ereg("[0-9]{4}", $_POST["empno"]))) {
        echo "ちゃんと入力してください<br>";
    }
    else {
                          :
?>

PHPでの対策方法 その2(DBのBIND機能を使う)

BINDの利用にもありますが、データベースのバインド機能を使うことで、SQLでの特殊文字(「'」や「%」など)をただの文字として扱うことができます。なお、当然ですが使用するデータベースがバインド機能をサポートしていないと使えないです。もし、バインド機能をサポートしているDBを使っているのであれば、是非お勧めします。

●PHP+Oracleでのバインド機能の使用例

<?php
                          :
        $sql = "UPDATE emp SET sal = :sal WHERE empno = :empno ";
        $stmt = OCIParse($conn, $sql);
        OCIBindByName($stmt, ":SAL", $_POST["sal"], -1);
        OCIBindByName($stmt, ":EMPNO", $_POST["empno"], -1);
        @OCIExecute($stmt);
                          :
?>

上記の2点を合わせると、以下のようなスクリプトなります。

●SQLIinjection02.php

<?php
    $conn = OCILogon("scott", "tiger", "orcl");

    if (!(isset($_POST["sal"]) && isset($_POST["empno"]))) {
        echo "従業員番号・給与を入力してください<br>";
    }
    else if (!(ereg("[0-9]{1,4}", $_POST["sal"]) && ereg("[0-9]{4}", $_POST["empno"]))) {
        echo "ちゃんと入力してください<br>";
    }
    else {
        $sql = "UPDATE emp SET sal = :sal WHERE empno = :empno ";
        $stmt = OCIParse($conn, $sql);
        OCIBindByName($stmt, ":SAL", $_POST["sal"], -1);
        OCIBindByName($stmt, ":EMPNO", $_POST["empno"], -1);
        @OCIExecute($stmt);
        if (!OCIError($stmt)) {
            OCICommit($conn);
            echo "commitしました<br>";
        }
        else {
            OCIRollback($conn);
            echo "rollbackしました<br>";
        }
    }

    $sql = "SELECT empno, sal FROM emp ORDER BY empno";
    $stmt = OCIParse($conn, $sql);
    OCIExecute($stmt);
    $ncols = OCINumCols($stmt);

    echo "<TABLE BORDER='1'>";
    echo "<TR>";
    for ( $i = 1; $i <= $ncols; $i++ ) {
        $column_name  = OCIColumnName($stmt,$i);
        echo "<TH>" . $column_name . "</TH>";
    }
    echo "</TR>";

    while(OCIFetch($stmt))
    {
        echo "<TR>";
        for ( $i = 1; $i <= $ncols; $i++ ) {
            $column_name  = OCIColumnName($stmt,$i);
            echo "<TD>" . OCIResult($stmt, $column_name) . "</TD>";
        }
        echo "</TR>";
    }

    echo "</TABLE>";

    OCIFreeStatement($stmt);
    OCILogoff($conn);
?>
<form action="SQLInjection02.php" method="post">
従業員番号<input type="text" name="empno">の給与を<input type="text" name="sal">に更新します<br>
<input type="submit">
</form>


About This Site |  Privacy Policy |  Contact
Copyright © 1999 - 2002 by Hideyuki SHIMOOKA all rights reserved.