ホームページ  >  記事  >  バックエンド開発  >  PHPでSQLインジェクションを防ぐ方法

PHPでSQLインジェクションを防ぐ方法

伊谢尔伦
伊谢尔伦オリジナル
2016-11-24 14:35:571335ブラウズ

問題の説明:
ユーザーが入力したデータが処理されずに SQL クエリ ステートメントに挿入された場合、アプリケーションは次の例のように SQL インジェクション攻撃を受ける可能性があります:

$unsafe_variable = $_POST['user_input'];

mysql_query("INSERT INTO ` table` (`column`) VALUES ('" . $unsafe_variable . "')");
ユーザーの入力は次のようになります:

value'); DROP TABLE table;-
その後、SQL クエリは次のようになります:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

SQL インジェクションを防ぐためにはどのような効果的な方法を採用する必要がありますか?

ベストアンサー (Theo から):
準備されたステートメントとパラメーター化されたクエリを使用します。準備されたステートメントとパラメータはそれぞれ解析のためにデータベース サーバーに送信され、パラメータは通常の文字として扱われます。このアプローチにより、攻撃者による悪意のある SQL の挿入が防止されます。 このメソッドを実装するには 2 つのオプションがあります:

1. PDO を使用する:

$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');  
   
$stmt->execute(array('name' => $name));  
   
foreach ($stmt as $row) {  
    // do something with $row  
}

2. mysqli を使用する:

$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');  
$stmt->bind_param('s', $name);  
   
$stmt->execute();  
   
$result = $stmt->get_result();  
while ($row = $result->fetch_assoc()) {  
    // do something with $row  
}

PDO
デフォルトで PDO を使用すると、MySQL データベースは実際のプリペアド ステートメントを実行できないことに注意してください。下に)。この問題を解決するには、準備されたステートメントの PDO エミュレーションを無効にする必要があります。 PDO を正しく使用してデータベース接続を作成する例は次のとおりです。

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');  
   
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);  
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

上記の例では、エラー モード (ATTR_ERRMODE) は必要ありませんが、追加することをお勧めします。このようにして、致命的なエラー (Fatal Error) が発生した場合、スクリプトの実行は停止されませんが、プログラマは PDOException をキャッチしてエラーを適切に処理できるようになります。 ただし、最初の setAttribute() 呼び出しが必要です。これにより、PDO によるプリペアド ステートメントのシミュレーションが無効になり、実際のプリペアド ステートメントが使用されます。つまり、MySQL がプリペアド ステートメントを実行します。これにより、ステートメントとパラメータが MySQL に送信される前に PHP によって処理されていないことが保証され、攻撃者による悪意のある SQL の挿入が防止されます。理由を理解するには、このブログ記事「PDO の抗注射原理の分析と PDO 使用時の注意事項」を参照してください。 PHP の古いバージョン (5.3.6 未満) では、PDO のコンストラクターで DSN を介して charset を設定できないことに注意してください。「charset パラメータをサイレントに無視する」を参照してください。

解析
前処理と解析のために SQL ステートメントをデータベース サーバーに送信するとどうなりますか?プレースホルダ (上記の例のように ? または :name) を指定して、データベース エンジンにフィルタリングする場所を伝えます。 execute を呼び出すと、準備されたステートメントは指定したパラメーター値と結合されます。 重要な点はここです。パラメータ値は SQL 文字列ではなく、解析された SQL ステートメントと結合されます。 SQL インジェクションは、SQL ステートメントの作成時に悪意のある文字列を含むスクリプトによってトリガーされます。したがって、SQL ステートメントとパラメーターを分離することで、SQL インジェクションのリスクを回避できます。送信するパラメータ値は通常の文字列として扱われ、データベース サーバーによって解析されません。上記の例に戻ると、$name 変数の値が 'Sarah'; DELETE FROM 従業員の場合、実際のクエリは、name フィールドの値が 'Sarah' である従業員内のレコードを検索することになります。 プリペアド ステートメントを使用するもう 1 つの利点は、同じデータベース接続セッションで同じステートメントを何度も実行した場合、解析されるのは 1 回だけになるため、実行速度が少し向上することです。 挿入方法を知りたい場合は、次の例を参照してください (PDO を使用):

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');  
   
$preparedStatement->execute(array('column' => $unsafeValue));


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。