|
間違いやご意見がありましたら、ご指摘下さい
「クロスサイトスクリプティングについて」に続き、IPA/ISECネタ第2弾です。それにしても、「ダイレクトSQLコマンドインジェクション」って長いですね。。。(^-^;
ダイレクトSQLコマンドインジェクション とは
ダイレクトSQLコマンドインジェクション攻撃とは,引数などのパラメタにSQL文を混ぜ込んでおき(インジェクション),プログラム内部でそのSQL文を実行させてしまう攻撃手法(IPAセキュリティセンター(IPA/ISEC)のセキュア・プログラミング講座から引用)のこと。
詳細については、IPA/ISECの以下のページに分かりやすく書かれています。
イメージとしては、「SQL版クロスサイトスクリプティング」というところでしょうか?SQL文を作成する際、where句のパラメータを指定することが多いと思いますが、パラメータ自体にSQLを埋め込まれた場合、意図しないSQLになってしまいそれが実行されてしまう、というものです。
以下のサンプルは、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>
|