この記事では、PHP で SQL インジェクションを防ぐ方法を例とともに分析します。参考のためにみんなで共有してください。具体的な分析は次のとおりです:
1. 問題の説明:
ユーザーが入力したデータが処理されずに SQL クエリ ステートメントに挿入された場合、次の例のように、アプリケーションは SQL インジェクション攻撃を受ける可能性があります。
コードをコピー
プリペアドステートメントとパラメータ化されたクエリを使用します。準備されたステートメントとパラメータはそれぞれ解析のためにデータベース サーバーに送信され、パラメータは通常の文字として扱われます。このアプローチにより、攻撃者による悪意のある SQL の挿入が防止されます。 このメソッドを実装するには 2 つのオプションがあります:
1. PDO を使用します:
コードをコピーします
コードをコピーします
デフォルトで PDO を使用すると、MySQL データベースが実際のプリペアド ステートメントを実行できないことに注意してください (理由については以下を参照)。この問題を解決するには、準備されたステートメントの PDO エミュレーションを無効にする必要があります。 PDO を正しく使用してデータベース接続を作成する例は次のとおりです:
コードをコピーします
4. 分析
前処理と解析のために SQL ステートメントをデータベース サーバーに送信するとどうなりますか?プレースホルダ (上記の例のように ? または :name) を指定して、データベース エンジンにフィルタリングする場所を伝えます。 execute を呼び出すと、準備されたステートメントは指定したパラメーター値と結合されます。 重要な点はここです。パラメータ値は SQL 文字列ではなく、解析された SQL ステートメントと結合されます。 SQL インジェクションは、SQL ステートメントの作成時に悪意のある文字列を含むスクリプトによってトリガーされます。したがって、SQL ステートメントとパラメーターを分離することで、SQL インジェクションのリスクを回避できます。送信するパラメータ値は通常の文字列として扱われ、データベース サーバーによって解析されません。上記の例に戻ると、$name 変数の値が 'Sarah'; DELETE FROM 従業員の場合、実際のクエリは、name フィールドの値が 'Sarah' である従業員内のレコードを検索することになります。 プリペアド ステートメントを使用するもう 1 つの利点は、同じデータベース接続セッションで同じステートメントを何度も実行した場合、解析されるのは 1 回だけになるため、実行速度が少し向上することです。 挿入方法を知りたい場合は、次の例を参照してください (PDO を使用):
この記事で説明した内容が皆様の PHP プログラミング設計に役立つことを願っています。
「いいね!」の多かった質問と回答を翻訳しました。質問: ユーザー入力を SQL ステートメントに直接挿入できる場合、アプリケーションは SQL インジェクション攻撃に対して脆弱になります。例: $unsafe_variable = $_POST['user_input']; INSERT INTO table (column) VALUES ( '" . $unsafe_variable . "')"); ユーザーは次のような値を入力できます: value'); DROP TABLE table;-- 、SQL ステートメントは次のようになります: INSERT INTO table (column) VALUES('value ') ; DROP TABLE table;-') (翻訳者注: この結果、テーブルが削除されます) この状況を防ぐにはどうすればよいでしょうか?回答: プリペアド ステートメントとパラメータ化されたクエリを使用します。これらの SQL ステートメントはデータベース サーバーに送信され、そのパラメーターはすべて個別に解析されます。この方法を使用すると、攻撃者が悪意のある SQL を挿入することは不可能になります。これを実現するには、主に 2 つの方法があります。 1. PDO を使用します。 $stmt = $pdo->prepare('SELECT * FROM従業員 WHERE name = :name'); $stmt->execute(array(':name') => $name)); foreach ($stmt as $row) { // $row を使用します: $stmt = $dbConnection->prepare('SELECT * FROM 従業員 WHERE name = ?' ); $stmt->bind_param('s', $name); $stmt->get_result(); $row = $result->fetch_assoc( )) { // $row で何かを行う } PDO PDO を使用して MySQL データベースにアクセスする場合、デフォルトでは実際のプリペアド ステートメントは使用されないことに注意してください。この問題を解決するには、シミュレートされた準備済みステートメントを無効にする必要があります。以下は、PDO を使用して接続を作成する例です。 $dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass'); ;setAttribute( PDO::ATTR_EMULATE_PREPARES, false); $dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 上の例では、エラー報告モードは必須ではありませんが、使用することをお勧めします。追加してください。このようにして、何か問題が発生した場合でもスクリプトは致命的なエラーで終了せず、PDO 例外をスローするため、開発者はエラーをキャッチする機会が得られます。ただし、最初の行の setAttribute() は必須であり、これにより PDO はシミュレートされた準備済みを無効にします... テキストの残りの部分>>
ユーザー入力が SQL ステートメントに直接挿入されるクエリである場合、アプリケーションは次の例のように SQL インジェクションに対して脆弱になります。 $unsafe_variable = $_POST['user_input']; mysql_query("INSERT INTO table ( column) VALUES ('" . $unsafe_variable . "')"); これは、ユーザーが VALUE"); のようなものを入力してクエリを作成できるためです: 準備されたステートメントとパラメータ化された SQL ステートメントを使用します。攻撃者が悪意を持って SQL を挿入することは不可能です。この目標を達成するには、基本的に 2 つのオプションがあります: 1. PDO (PHP データ オブジェクト) を使用します: $stmt = $pdo->prepare ('SELECT * FROM 従業員 WHERE name = :name'); $stmt->execute(array(':name' => $name)); foreach ($stmt as $row) { // $ で何かを行う行 }2. mysqli:$stmt = $dbConnection->prepare('SELECT * FROM 従業員 WHERE name = ?'); $stmt->bind_param('s', $name) ; を使用します。 (); $result = $stmt->get_result(); while ($row = $result->fetch_assoc()) { // $row で何かを行う }PDO(PHP Data Object ) 実際のプリペアド ステートメントは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);エラー モード ERRMODE は、上記の例では厳密には必須ではありませんが、追加することをお勧めします。このメソッドは、致命的なエラーが発生した場合でもスクリプトを停止しません。そして、開発者にエラー (PDOException がスローされたとき) を捕捉する機会を与えます。 setAttribute() 行は必須で、エミュレートされたプリペアド ステートメントを無効にし、実際のプリペアド ステートメントを使用するように PDO に指示します。これにより、ステートメントと値が MySQL データベース サーバーに送信される前に PHP によって解析されなくなります (攻撃者が悪意のある SQL を挿入する機会はありません)。もちろん、特別な注意を払って、コンストラクター オプションで文字セット パラメーターを設定することもできます。 「古い」PHP バージョン (5.3.6) では、DSN の文字セット パラメータが無視されます。ここで最も重要なことは、パラメータ値が SQL 文字列ではなく、プリコンパイルされたステートメントと結合されることです。SQL インジェクションの動作原理は、欺瞞によって作成された SQL スクリプトに悪意のある文字列が含まれていることです。 >>