English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

SQLインジェクション(Injection)

このチュートリアルでは、一般的なデータベースの脆弱性を修正する方法を学びます。

SQLインジェクションとは何ですか?

SQLインジェクションは、攻撃者がブラウザを通じてアプリケーションサーバーにデータを入力する際(例えば、Webフォームの入力)に悪意のあるSQLコードを注入または実行する攻撃です。

これは、ユーザーの電話番号、メールアドレス、クレジットカード情報などの機密情報を公開するために使用できます。攻撃者はこれを使用して認証プロセスを迂回し、データベース全体へのアクセス権を取得することもできます。それがどのように機能するかを見てみましょう。

SQLインジェクションはどのように機能するのか

以下のSQLステートメントは、Webアプリケーションでユーザー名とパスワードを使用してユーザーを認証するシンプルな例です。

SELECT * FROM users WHERE username='username_val' AND password='password_val';

ここでは、username_valおよびpassword_valそれぞれ、ユーザーが入力したユーザー名とパスワードを示します。ユーザーが「john」というユーザー名を入力し、「 123「パスワード」といった値があれば、結果のステートメントは以下のようになります:

SELECT * FROM users WHERE username='john' AND password='123';

しかし、ユーザーが攻撃者であれば、入力フィールドに有効なユーザー名とパスワードを入力せずに以下のような値を入力することを考えてみましょう:' OR 'x'='x

この場合、上記のSQLクエリは以下のように構築されます:

SELECT * FROM users WHERE username='' OR 'x'='x' AND password='' OR 'x'='x';

このステートメントは有効なSQLステートメントであり、WHERE 'x'='x'は常にtrueになるため、クエリは以下を返しますusersテーブルのすべての行です。攻撃者が簡単にデータベースのすべての機密情報にアクセスできるようにする小技を見ることができます。

もしusersテーブルが大きく、数百万行を含んでいる場合、この単一のクエリはシステムリソースがオーバーウェイトされて、サービス拒否攻撃(DoS攻撃)が発生し、あなたのアプリケーションが正当なユーザーに対して利用不可能になる可能性があります。

警告:あなたのスクリプトがDELETEまたはUPDATEクエリを実行する場合、SQLインジェクションの影響を無視したり、もっと悪い結果になる可能性があります。攻撃者はデータをテーブルから削除したり、すべての行を永続的に変更することができます。

SQLインジェクションを防ぐ

常にユーザー入力を確認し、仮定をせずに行ってください。ユーザー入力からSQL文を直接構築することは避けてください。PHPとMySQLを使用している場合、mysqli_real_escape_string()関数を使用してSQL文で使用できる合法なSQL文字列を作成できます。

これはPHPとMySQLを使用してユーザー認証を行う非常に基本的な例で、ユーザーからの入力を取得する際にSQLインジェクションを防ぐ方法を示しています。

<?php
// セッション開始
session_start();
 
/* MySQLサーバーへの接続を試みます。
   MySQLサーバーがデフォルト設定で実行されていると仮定します(ユーザー'root'にはパスワードがありません) */
$link = mysqli_connect("localhost", "root", "", "demo");
 
// 接続を確認してください
if($link === false){
    die("エラー:データベースに接続できません。");
}
 
// ユーザー入力をエスケープして安全性を確保します
$username_val = mysqli_real_escape_string($link, $_POST['username']);
$password_val = mysqli_real_escape_string($link, $_POST['password']);
 
if(isset($username_val, $password_val)){
    // クエリの実行を選択しようとしました
    $sql = "SELECT" * FROM users WHERE username='" . $username_val . "' AND password='" . $password_val . "'";
    if($result = mysqli_query($link, $sql)){
        if(mysqli_num_rows($result) == 1{
            // ユーザーは認証が完了しました、ここで操作を行ってください
            $row = mysqli_fetch_array($result);
            /*セッション変数の値を保存します。
               これにより、後で同じセッション参照でアクセスできます。 */
            $_SESSION['user_id'] = $row['user_id'];
            $_SESSION['first_name'] = $row['first_name'];
            header('Location: welcome.html');
        }
            echo "エラー!無効なユーザー名またはパスワード。";
        }
    }
        echo "エラー:何か問題が発生しました。もう一度試してください。";
    }
}
 
// Close connection 接続を閉じる
mysqli_close($link);
?>

ヒント:テストアプリケーションが受け取るデータのサイズとタイプまたは内容を確認し、システムリソースが悪用されるのを防ぐために適切な制限を実施します。