SQL Injection

❮ 前章へ 次章へ ❯

SQL インジェクションは、データベースを破壊する可能性があります。


Web ページ内の SQL

前の章では、SQLを使用してデータベースからのデータ取得(および更新)について学習しました。

web ページにデータを表示するため SQL を使用する場合、Web ユーザが独自の検索値を入力できるようにするのが一般的です。

SQL 文はテキストだけなので、ごく小さなコンピュータコードで、 選択データを提供するための SQL 文を動的に変更するのは簡単です:

サーバ・コード

txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = " + txtUserId;

上記例は、選択するための文字列へ、変数 (txtUserId) を使用した select 文を作成しています。 変数には、ページへのユーザ入力(リクエスト)から取出されます。

この章の後段部分では、SQL 文の中でユーザ入力を使用することへの潜在的な危険性を説明します。


SQL インジェクション

SQL インジェクションは、悪意のあるユーザーが、web ページの入力を介して、 SQL 文に SQL コマンドを挿入できるようにする技術です。

挿入された SQL コマンドは、SQL 文を変更し、web アプリケーションのセキュリティを破壊します。


「1=1 は常に true」に基づいた SQL インジェクション

上記の例をもう一度見てください。

コードの元々の目的は、指定のユーザ id を持ったユーザを選択するための SQL 文であったものとします​​。

ユーザの入力 "間違い" を防止するものが何もない場合、ユーザは、次のような "賢い" 入力をすることができます:

UserId:

サーバの結果

SELECT * FROM Users WHERE UserId = 105 or 1=1

上の SQL は妥当です。これは、WHERE 1=1 は常に真なので、Users テーブルから全ての行が返されます。

上の例が、危険なように見えるのでしょうか? Users テーブルに名前とパスワードが含まれている場合はどうでしょう?

上の SQL 文は、次に同じです:

SELECT UserId, Name, Password FROM Users WHERE UserId = 105 or 1=1

賢いハッカーは、単に入力ボックスに 105 or 1=1 を挿入するだけで、 データベース内のすべてのユーザ名とパスワードへのアクセスができるようになります。


「""="" は常に true」に基づいた SQLインジェクション

ここに、Web サイトへのユーザ・ログインを検証する一般的な構文があります:

User Name:

Password:

Server Code

uName = getRequestString("UserName");
uPass = getRequestString("UserPass");

sql = "SELECT * FROM Users WHERE Name ='" + uName + "' AND Pass ='" + uPass + "'"

賢いハッカーは、単に User Name および Password テキストボックスに、" または ""=" を挿入するだけで、 データベース内のユーザー名とパスワードへのアクセスができるようになります。

サーバにおけるコードは、次のような妥当な SQL 文が作成されます:

Result

SELECT * FROM Users WHERE Name ="" or ""="" AND Pass ="" or ""=""

結果の SQL は妥当です。 WHERE ""="" は常に true なので、Users テーブルからは全ての行が返されます。


バッチ SQL 文に基づいた SQL インジェクション

ほとんどのデータベースは、セミコロンで区切った、バッチ SQL 文をサポートしています。

SELECT * FROM Users; DROP TABLE Suppliers

上の SQL は、Users テーブルの全行を返した後、Suppliers テーブルを削除します。

サーバにおけるコードを次のように作成した場合:

サーバ・コード

txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = " + txtUserId;

および、次の入力を作成:

User id:

サーバでのコードは、次ような妥当な SQL 文が作成されます:

結果

SELECT * FROM Users WHERE UserId = 105; DROP TABLE Suppliers

保護用のパラメータ

一部の Web 開発者は、SQL インジェクション攻撃を防ぐために、 SQL 入力内を検索する単語や文字の "ブラックリスト" を使用しています。

これは余り良いアイデアではありません。これらの多くの単語(delete、drop など)や 文字(セミコロン、引用符など)は、一般的な言語で使用されるべきで、多数のタイプが入力に許容されるべきです。

(実際に、データベース・フィールドに入力する SQL 文は完全に有効である必要があります。)

SQL インジェクション攻撃から web サイトを保護する唯一の実証された方法は、SQ Lパラメータを使用することです。

SQL パラメータは、制御された方法で、実行時に SQL クエリに追加される値です。

ASP.NET Razor 例

txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = @0";
db.Execute(txtSQL,txtUserId);

パラメータは、SQL 文内において @ で表わされることに注意してください。

SQL エンジンは、その列用の正確なものである保証するため、および文字の示す通りに扱われ、 SQL の一部として実行されないために、各パラメータをチェックします。

他の例

txtNam = getRequestString("CustomerName");
txtAdd = getRequestString("Address");
txtCit = getRequestString("City");
txtSQL = "INSERT INTO Customers (CustomerName,Address,City) Values(@0,@1,@2)";
db.Execute(txtSQL,txtNam,txtAdd,txtCit);

SQL インジェクションを防ぐことを学びました。web サイトの最も脆弱なものの 1 つです。


次の例は、いくつかの一般的な web 言語におけるパラメータ化クエリの作成方法を示しています。

ASP.NET の場合の SELECT 文

txtUserId = getRequestString("UserId");
sql = "SELECT * FROM Customers WHERE CustomerId = @0";
command = new SqlCommand(sql);
command.Parameters.AddWithValue("@0",txtUserID);
command.ExecuteReader();

>ASP.NET の場合の INSERT INTO 文:

txtNam = getRequestString("CustomerName");
txtAdd = getRequestString("Address");
txtCit = getRequestString("City");
txtSQL = "INSERT INTO Customers (CustomerName,Address,City) Values(@0,@1,@2)";
command = new SqlCommand(txtSQL);
command.Parameters.AddWithValue("@0",txtNam);
command.Parameters.AddWithValue("@1",txtAdd);
command.Parameters.AddWithValue("@2",txtCit);
command.ExecuteNonQuery();

PHP の場合の INSERT INTO 文:

$stmt = $dbh->prepare("INSERT INTO Customers (CustomerName,Address,City)
VALUES (:nam, :add, :cit)");
$stmt->bindParam(':nam', $txtNam);
$stmt->bindParam(':add', $txtAdd);
$stmt->bindParam(':cit', $txtCit);
$stmt->execute();

❮ 前章へ 次章へ ❯