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

推薦する

IDEA が MySQL ポート番号占有に接続できない問題の解決方法

コマンドラインでMYSQLに正常にログインでき、NavicatもMySQLに正常に接続できますが、I...

VMware15 centos7 ブリッジモード ssh に突然アクセスできなくなる問題を解決する

仮想マシンに独自の LAN IP を持たせたいので、テストを容易にするためにブリッジを使用します。 ...

mysql はインデックスを無効にしますか?

mysql の IN はインデックスを無効にしますか?しませんよ! 結果をご覧ください: mysq...

Docker ビルド kubectl イメージ実装手順

プログラムサービスがgitlab ci/cdと統合されたk8sを使用してデプロイされている場合、gi...

Docker の NFS-Ganesha イメージを使用して NFS サーバーを構築する詳細なプロセス

目次1. NFS-Ganeshaの紹介2. NFS-Ganeshaの設定3. NFS-Ganesha...

Vueカスタムコンポーネントはイベント修飾子を使用してピットレコードを踏む

序文今日、自作のコンポーネントを使っていたところ、突然、長い間忘れていたバブリングイベントに遭遇しま...

Vueは小さな天気予報アプリケーションを実装します

これは私が Vue フレームワークを独学していたときに真似したウェブサイトです。いくつかの都市の天気...

シームレスなトークンリフレッシュを実現する方法

目次1. 需要方法1方法2方法3 2. 実装3. 問題解決質問1: トークンの複数回の更新を防ぐ方法...

ubuntu15.10 での hadoop2.7.2 の詳細なインストールと設定

Linux での Hadoop インストール チュートリアルはインターネットや書籍に多数ありますが、...

初心者がHTMLタグを学ぶ(2)

初心者は、いくつかの HTML タグを理解することで HTML を学習できます。この入門書は、初心者...

Vueでデータを読み取るためにこれを悪用しないでください

目次序文1. これを使用してデータ内のデータを読み取るプロセス2. Dep.target はいつ存在...

初心者向けウェブサイト構築ガイド⑦:美しいウェブサイトを作るのはとっても簡単

私はかつて、ウェブサイトを一度も構築したことのない人々が、初心者向けのウェブサイト構築方法に関する私...

JavaScript でグレイウルフのポットビーティングゲームを実装

1. プロジェクト文書 2. ページレイアウトにHTMLとCSSを使用するHTML部分 <di...

Vue 初心者ガイド: 環境の構築と開始方法

目次初期ビューVue開発環境の構築Vueインスタンスの作成Vue テンプレート構文Vue データバイ...