検索

ホームページ  >  に質問  >  本文

PHP での SQL インジェクションを防ぐにはどうすればよいですか?

<p>ユーザー入力が変更されずに SQL クエリに挿入される場合、次の例に示すように、アプリケーションは SQL インジェクションに対して脆弱になります。 <pre class="lang-php prettyprint-override"><code>$unsafe_variable = $_POST['user_input']; mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')"); </code></pre> <p>これは、ユーザーが <code>value'); DROP TABLE table;--</code> のようなものを入力できるため、クエリは次のようになります。 <pre class="brush:php;toolbar:false;">INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')</pre> <p>これを防ぐにはどうすればよいでしょうか? </p>
P粉128563140P粉128563140450日前608

全員に返信(2)返信します

  • P粉238355860
  • P粉283559033

    P粉2835590332023-09-01 11:53:19

    どのデータベースを使用する場合でも、SQL インジェクション攻撃を回避する 正しい 方法は、 データを SQL から分離することです これにより、データは依然としてデータのままであり、 >決して SQL パーサーによってコマンドとして解釈されません。適切にフォーマットされたデータ部分を含む SQL ステートメントを作成することは可能ですが、 詳細がまったく理解できない場合は、常に プリペアド ステートメントとパラメーター化されたクエリを使用する必要があります。 は、パラメーターとは別にデータベース サーバーに送信され、データベース サーバーによって解析される SQL ステートメントです。これにより、攻撃者が悪意のある SQL を挿入することは不可能になります。

    これを実現するには基本的に 2 つのオプションがあります:

    1. PDO の使用 (サポートされているデータベース ドライバーの場合): リーリー

    2. MySQLi

      の使用 (MySQL の場合): PHP 8.2 以降では、
      execute_query() を使用して、1 つのメソッドで準備、パラメータのバインド、SQL ステートメントの実行を行うことができます。 リーリー < code>PHP8.1 まで:

      リーリー

      MySQL 以外のデータベースに接続している場合は、2 番目のドライバー固有のオプションを参照できます (たとえば、PostgreSQL の場合は

      pg_prepare()
    3. および
    pg_execute()

    ) 。 PDO はユニバーサル オプションです。 接続を正しく設定してください< /code>

    PDO

    PDO

    を使用して MySQL データベースにアクセスする場合、

    実際の

    プリペアド ステートメント はデフォルトでは使用されないことに注意してください。この問題を解決するには、準備されたステートメントのシミュレーションを無効にする必要があります。 PDO を使用して接続を作成する例は次のとおりです。 リーリー 上記の例では、エラー モードは厳密には必要ありませんが、 追加することをお勧めします。このように、PDO は PDOException をスローすることで、すべての MySQL エラーを通知します。

    ただし、 forcing は最初の setAttribute() 行で、シミュレートされたプリペアド ステートメントを無効にし、

    実際の

    プリペアド ステートメント ステートメントを使用するように PDO に指示します。これにより、ステートメントと値が MySQL サーバーに送信される前に PHP によって解析されなくなります (攻撃者になる可能性のある者に悪意のある SQL を注入する機会を与えません)。 コンストラクターのオプションで 文字セット を設定できますが、PHP の「古い」バージョン (5.3.6 より前) は文字セットを黙って無視することに注意することが重要です。パラメーター ###。

    Mysqli

    mysqli の場合も同じルーチンに従う必要があります: リーリー イラスト

    prepare

    に渡す SQL ステートメントは、データベース サーバーによって解析され、コンパイルされます。パラメーター (

    ?
    または上記の例の

    :name

    などの名前付きパラメーター) を指定して、データベース エンジンにフィルターする場所を伝えます。次に、

    execute を呼び出すと、準備されたステートメントが指定したパラメーター値と結合されます。 ここで重要なのは、パラメーター値が SQL 文字列ではなく、コンパイルされたステートメントと組み合わされるということです。 SQL インジェクションは、データベースに送信する SQL を作成するときに、スクリプトをだまして悪意のある文字列を含めることによって機能します。したがって、実際の SQL をパラメータとは別に送信することで、予期しない結果が生じるリスクを制限できます。

    準備されたステートメントを使用するときに送信するパラメーターはすべて文字列として扱われます (ただし、データベース エンジンが最適化を行う可能性があるため、パラメーターは最終的に数値として扱われることもあります)。上記の例では、$name 変数に 'Sarah'; DELETE FROMEmployees が含まれている場合、結果は単に検索文字列 "'Sarah'; DELETE FROMEmployees" になります。 空のテーブルが残ることはありません。

    プリペアド ステートメントを使用するもう 1 つの利点は、同じセッション内で同じステートメントを複数回実行しても、解析とコンパイルが 1 回だけ行われるため、速度が向上することです。

    ああ、挿入方法を尋ねられたので、ここに例を示します (PDO を使用):

    リーリー

    準備されたステートメントは動的クエリに使用できますか?

    プリペアド ステートメントをクエリ パラメーターとともに使用することはできますが、動的クエリ自体の構造はパラメーター化できず、特定のクエリ関数もパラメーター化できません。

    これらの特定のシナリオの場合、最適なアプローチは、ホワイトリスト フィルターを使用して、可能な値を制限することです。

    リーリー

    返事
    0
  • キャンセル返事