PDO を使用して SQL インジェクションを防ぐ原理の分析

PDO を使用して SQL インジェクションを防ぐ原理の分析

序文

この記事では、SQL インジェクションを回避するために pdo の前処理メソッドを使用します。詳しい紹介を見てみましょう。

PHP マニュアルの「PDO - 準備されたステートメントとストアド プロシージャ」の項:

  • より成熟したデータベースの多くは、準備されたステートメントの概念をサポートしています。準備済みステートメントとは何ですか?変数パラメータを使用してカスタマイズできる、実行する SQL のコンパイル済みテンプレートと考えてください。準備されたステートメントには、2 つの大きな利点があります。
  • クエリは一度だけ解析 (または前処理) する必要がありますが、同じまたは異なるパラメータを使用して複数回実行できます。クエリが準備されると、データベースはクエリを実行するためのプランを分析、コンパイル、および最適化します。複雑なクエリの場合、このプロセスには長い時間がかかり、同じクエリを異なるパラメータで複数回繰り返す必要がある場合は、アプリケーションの速度が大幅に低下する可能性があります。準備されたステートメントを使用すると、解析/コンパイル/最適化のサイクルの繰り返しを回避できます。つまり、準備されたステートメントは使用するリソースが少なくなるため、実行速度が速くなります。
  • 準備されたステートメントに提供されるパラメータは引用符で囲む必要はありません。ドライバがこれを自動的に処理します。アプリケーションが準備されたステートメントのみを使用する場合、SQL インジェクションが発生しないことを保証できます。 (ただし、クエリの他の部分がエスケープされていない入力から構築されている場合は、SQL インジェクションのリスクが依然として残ります)。
  • 準備済みステートメントは非常に便利なので、ドライバーがサポートしていない場合は PDO がそれをエミュレートするという唯一の機能があります。これにより、データベースにそのような機能があるかどうかに関係なく、アプリケーションは同じデータ アクセス パターンを使用できるようになります。

上記の 2 つの利点は次のとおりです。

1. まず、MySQL のストアド プロシージャについて説明します。MySQL5 ではストアド プロシージャ機能が導入されました。ストアド プロシージャが作成されると、データベースはすでにそれを解析して最適化していました。次に、ストアド プロシージャが実行されると、そのストアド プロシージャのコピーがメモリ内に保持されるため、次に同じストアド プロシージャが実行されたときに、メモリから直接読み取ることができます。 MySQL ストアド プロシージャの使用については、https://www.jb51.net/article/7032.htm を参照してください。

PDO の場合も、原理は同じですが、PDO は EMULATE_PREPARES (シミュレートされた前処理) をサポートしており、これは PDO ドライバーによってローカルで完了します。同時に、ローカルのシミュレートされた前処理を使用せず、MySQL に完了させることもできます。これら 2 つの状況については、以下で説明します。

2. SQL インジェクションを防ぐために、tcpdump と wireshark を使用してパケットをキャプチャし、分析しました。

仮想マシン上でコードを実行して、リモート MySQL へのリクエストを開始します。

<?php

$pdo = 新しい PDO ("mysql:host=10.121.95.81;dbname=thor_cms;charset=utf8", "root","qihoo@360@qihoo");

$st = $pdo->prepare("id =? かつ uid = ?" の場合、share から * を選択します);

$id = 6;
$uid = 521;

$st->bindParam(1, $id);
$st->bindParam(2, $uid);

$st->execute();
$ret = $st->fetchAll();

print_r($ret);

tcpdump を介してパケットをキャプチャしてファイルを生成します。

tcpdump -ieth0 -A -s 3000 ポート 3306 -w ./mysql.dump

sz mysql.dump

Wireshark 経由でファイルを開きます:

全体のプロセスを見ることができます: 3ウェイハンドシェイク - ログイン要求 - 要求クエリ - 要求終了

リクエスト クエリ パッケージを見ると、次のことがわかります。

はぁ?これも SQL ステートメントを連結しているのではないですか?

実際、これは、mysql_real_escape_string を使用して文字列をエスケープし、それを SQL ステートメントに連結する方法と変わりません。PDO ローカル ドライバーがエスケープを完了するだけです (EMULATE_PREPARES)

この場合、SQL インジェクションは依然として可能です。つまり、クエリを操作するために PHP で pdo prepare の mysql_real_escape_string がローカルに呼び出され、ローカルのシングルバイト文字セットが使用され、マルチバイトでエンコードされた変数を渡すと、SQL インジェクションの脆弱性が依然として発生する可能性があります (これは PHP 5.3.6 より前のバージョンの問題の 1 つであり、PHP 5.3.6 以降にアップグレードし、PDO を使用するときに DSN 文字列で文字セットを指定することが推奨される理由です)。

PHP 5.3.6 より前のバージョンでは、次のコードによって SQL インジェクションの問題が発生する可能性があります。

$pdo->query('GBKの名前を設定'); 

$var = chr(0xbf) . chr(0x27) . " OR 1=1 /*"; 

$query = "SELECT * FROM info WHERE name = ?"; 

$stmt = $pdo->prepare($query); 

$stmt->execute(配列($var));

正しいエスケープは、MySQL サーバーの文字セットを指定し、変数を MySQL サーバーに送信して文字エスケープを完了することです。

では、PHP のローカル エスケープを無効にして、MySQL サーバーにエスケープを実行させるにはどうすればよいでしょうか?

PDO には PDO::ATTR_EMULATE_PREPARES というパラメータがあり、PHP ローカル シミュレート準備を使用するかどうかを示します。このパラメータのデフォルトは true です。これを false に変更してパケットをキャプチャしてみましょう。

コードの最初の行の後に追加します

PDO::ATTR_EMULATE_PREPARES 属性を false に設定します。

tcpdump を使用してパケットを再度キャプチャします。Wireshark では次のことがわかります。

PHPはSQL文を送信するために準備-実行メソッドを使用する

今回は変数エスケープ処理はMySQLサーバーによって実行されます。

変数と SQL テンプレートは 2 回送信されるため、SQL インジェクションの問題はありませんが、明らかに送信が 1 回多く発生します。これは、php5.3.6 以降では不要です。

PDO の使用に関する注意事項

1. PHP を 5.3.6 以降にアップグレードします。実稼働環境では、PHP 5.3.9 以降または PHP 5.4 以降にアップグレードすることを強くお勧めします。PHP 5.3.8 には、致命的なハッシュ衝突の脆弱性があります。

2. PHP 5.3.6 以降を使用している場合は、PDO DSN で charset 属性を指定してください。 5.3.6 未満: $dbh = new PDO($dsn,$user,$pass,array(PDO::MYSQL_ATTR_INIT_COMMAND => "set names utf8"));

3. PHP 5.3.6 以前を使用している場合は、PDO::ATTR_EMULATE_PREPARES パラメータを false に設定します (つまり、MySQL サーバーが変数を処理するようにします)。ローカルでシミュレートされた準備を使用する場合でも、MySQL サーバーの準備を呼び出す場合でも、PHP 5.3.6 以降のバージョンではこの問題がすでに処理されています。

4. PHP 5.3.6 以前を使用している場合、Yii フレームワークはデフォルトで ATTR_EMULATE_PREPARES の値を設定しないため、データベース構成ファイルで emulatePrepare の値を false に指定してください。

注記:

1. DSN で文字セットを指定する場合、セット名 <charset> を実行する必要があるのはなぜですか?

実際、セット名 <charset> には 2 つの機能があります。

MySQLサーバーにクライアント(PHPプログラム)が送信したエンコーディングを通知する

MySQLサーバーにクライアントが結果に必要とするエンコーディングを通知する

つまり、データ テーブルが gbk 文字セットを使用し、PHP プログラムが UTF-8 エンコーディングを使用する場合、クエリを実行する前に set names utf8 を実行して、プログラムでエンコーディングを変換することなく、MySQL サーバーに正しいエンコーディングを使用するように指示することができます。この方法では、UTF-8 エンコーディングで MySQL サーバーにクエリを送信すると、結果も UTF-8 エンコーディングになります。これにより、プログラム内でのエンコード変換の問題がなくなります。文字化けは発生しませんのでご安心ください。

では、DSN で文字セットを指定する目的は何でしょうか? これは、ローカル ドライバーがエスケープ時に指定された文字セットを使用するように PDO に指示するだけです (MySQL サーバー通信の文字セットは設定しません)。MySQL サーバー通信の文字セットを設定するには、set names <charset> コマンドも使用する必要があります。

2. PDO::ATTR_EMULATE_PREPARES 属性を false に設定したことによる殺人事件: http://my.oschina.net/u/437615/blog/369481

要約する

上記はこの記事の全内容です。この記事の内容が皆さんの勉強や仕事に一定の参考学習価値を持つことを願っています。ご質問があれば、メッセージを残してコミュニケーションしてください。123WORDPRESS.COM を応援していただきありがとうございます。

以下もご興味があるかもしれません:
  • PDO を使用して PHP で MySQL をクエリすることで SQL インジェクションのリスクを回避する方法
  • PHP での MySQL 接続における PDO の使用法の詳細な説明
  • PHP を使用して PDO で SQL データベースに接続し、クエリを実行する方法
  • PHPはPDOのmysqlデータベース操作クラスを実装します
  • PDO でパラメータ化されたクエリ SQL を使用する
  • PHPはPDOに基づく強力なMYSQLカプセル化クラスインスタンスを実装します
  • PHP が PDO を使用して SQL 文を実行する方法の分析

<<:  Apache ab同時負荷ストレステストの実装方法

>>:  デプロイから基本操作までDocker Swarm

推薦する

HTML テーブルの空白セル補完を実装する方法

私が初めて Web 開発を独学で学んだ頃は、いわゆる DIV/CSS レイアウトはなく、テーブル レ...

Linux で特殊文字のファイル名やディレクトリを削除する方法

inode番号でファイルを削除するまずls -iを使用して、削除するファイルのinode番号を見つけ...

Lvs+Nginx クラスターを使用して高並列アーキテクチャを構築する例

目次1. Lvsの紹介2. Lvs負荷分散モード2.1 NAT 2.2 ターン2.3 DRモード3....

n 個のコンテナ要素による無限スクロールの実装コード

シナリオ最大 10000 要素のリストを正しくレンダリングする方法。無限ドロップダウン読み込みテクノ...

MySQL 独立インデックスと共同インデックスの選択

複数列のインデックスについては、理解が不足していることがよくあります。よくある間違いは、多数の列に独...

ウェブページのCSSの優先順位について詳しく説明します

CSS の優先順位について話す前に、CSS とは何か、CSS が何に使用されるのかを理解する必要があ...

React Nativeの起動プロセスの詳細分析

はじめに: この記事ではreact-native-cliで作成したサンプル プロジェクト (Andr...

CSS でよく発生する問題の整理 (ロゴのハッキング/コンテナの固定/画像の垂直方向の中央揃え)

1. IEブラウザモードハックロゴ1. CSSハックロゴコードをコピーコードは次のとおりです。 ie...

MySql 8.0 と対応するドライバー パッケージの一致に関する注意事項

MySql 8.0 対応ドライバパッケージのマッチングMySql データベースをバージョン 8.0 ...

CSS を使用して親コンテナ div を img 画像で埋め、コンテナのサイズを調整する方法

ページに複数の画像を導入すると、画像のサイズがばらつくことがあります。しかし、それらを一貫したサイズ...

HTML リスト タグ dl、ul、ol の使用例

コードをコピーコードは次のとおりです。 <!--リストタグ: <dl>: 階層リス...

jwtを使用してノードによって生成されたトークンをどこに保存するかについての簡単な説明

A: 通常はクライアントに保存されます。 jwt または JSON Web Token は、リクエス...

フローティングメニューを実装するjQueryプラグイン

毎日jQueryプラグインを学ぶ - フローティングメニュー、参考までに、具体的な内容は次のとおりで...

QQtabBar による CSS 命名仕様 BEM の詳細な紹介

QQtabBar の BEMまず、BEMとはどういう意味でしょうか? BEM は、ブロック、要素、修...