PDO が合理的かつ正しく使用されている限り、基本的に SQL インジェクションを防止できることは誰もが知っています。この記事は主に次の 2 つの質問に答えます。
なぜ mysql_connect の代わりに PDO を使用するのですか?なぜ PDO はアンチインジェクションなのですか?
PDO アンチインジェクションを使用する際に特に注意すべきことは何ですか?
1. PDO の使用を優先する必要があるのはなぜですか?
PHP マニュアルには明確に記載されています:
プリペアド ステートメントのパラメータを引用符で囲む必要はありません。アプリケーションがプリペアド ステートメントのみを使用する場合、開発者は SQL インジェクションが発生しないことを確認できます (ただし、クエリの他の部分が使用されている場合)。エスケープされていない入力で構築されているため、SQL インジェクションは依然として可能です)。
同時にPHPマニュアルに警告メッセージが記載されています
データベースクエリを実行する前に、SQL 内の select、union、... などのキーワードをクリーンアップする方法もあります。このアプローチは明らかに間違った対処方法であり、提出されたテキストに学生組合が含まれている場合、置き換えによって元の内容が改ざんされ、無実の人々が無差別に殺害されることになるため、お勧めできません。
2. PDO が SQL インジェクションを防止できるのはなぜですか?
まず次の PHP コードを見てください:
環境は以下の通りです
PHP 5.4.7
MySQL プロトコル バージョン 10
MySQL サーバー 5.5.27
PHP と MySQL サーバー間の通信の詳細を徹底的に理解するために、調査用に特別に Wireshark を使用してパケットをキャプチャしました。次に示すように、Wireshak をインストールした後、フィルター条件を tcp.port==3306 に設定しました。
このように、不要な干渉を避けるために、mysql 3306 ポートとの通信データのみが表示されます。
Wireshak は wincap ドライバーに基づいており、ローカル ループバック インターフェイスでのリッスンをサポートしていないことに注意することが重要です (PHP を使用してローカル mysql に接続しても、リッスンすることはできません) 他のマシン (仮想マシン) に接続してください。ブリッジされたネットワークを使用することも可能です) テスト用の MySQL。
次に、PHP プログラムを実行します。リスニング結果は次のとおりです。PHP は単に SQL を MySQL サーバーに直接送信しているだけであることがわかりました。
実際、これは mysql_real_escape_string を使用して文字列をエスケープし、SQL ステートメントに結合するのと何ら変わりません (明らかに、この場合でも SQL インジェクションは可能です)。 PHP がクエリを操作するために pdo prepare で mysql_real_escape_string をローカルに呼び出すと、ローカルのシングルバイト文字セットが使用され、マルチバイトでエンコードされた変数を渡すと、依然として SQL インジェクションの脆弱性が発生する可能性があると言われています (php バージョン 5.3.6 より前の場合)この問題の 1 つは、PDO を使用する場合、php 5.3.6 以降にアップグレードし、DSN 文字列 で文字セットを指定することが推奨される理由を説明しています。
PHP 5.3.6 より前のバージョンの場合、次のコードは依然として SQL インジェクションの問題を引き起こす可能性があります:
コードをコピーします
それでは、PHP ローカル エスケープを無効にして、MySQL Server にエスケープさせるにはどうすればよいでしょうか?
PDO には PDO::ATTR_EMULATE_PREPARES という名前のパラメータがあり、PHP を使用してローカルでの準備をシミュレートするかどうかを示します。このパラメータのデフォルト値は不明です。先ほど確認したパケット キャプチャと分析の結果によると、PHP 5.3.6 以降は依然としてデフォルトでローカル変数を使用し、それらを SQL に結合して MySQL サーバーに送信します。この値を false に設定して効果を試してみます。次のコード内:
コードをコピーします見ましたか?これが魔法です。今回は、PHP が SQL テンプレートと変数を 2 回に分けて MySQL に送信し、MySQL が変数のエスケープを完了することがわかります。 SQL インジェクションの問題は解決されましたが、DSN で次のように charset 属性を指定する必要があります:
コードをコピーします
コードは次のとおりです:
このようにして、SQL インジェクションの問題を根本的に解決できます。これについてよくわからない場合は、zhangxugg@163.com に電子メールを送信して、一緒に話し合うことができます。
3. PDOを使用する際の注意点
上記の点を理解した後、PDO を使用して SQL インジェクションを防ぐためのいくつかの注意事項をまとめることができます。
1. php を 5.3.6 以降にアップグレードします。実稼働環境では、php 5.3.9 以降および php 5.4 以降にアップグレードすることを強くお勧めします。2. php 5.3.6以降を使用している場合は、PDOのDSNにcharset属性を指定してください
3. PHP 5.3.6 以前のバージョンを使用している場合は、PDO::ATTR_EMULATE_PREPARES パラメーターを false に設定します (つまり、ローカルを使用しているかどうかにかかわらず、変数処理は MySQL によって実行されています)。シミュレーションの準備または mysql サーバーの準備を呼び出すことができます。 DSN での文字セットの指定は無効であり、セット名
4. PHP 5.3.6 以前のバージョンを使用している場合、Yii フレームワークはデフォルトで ATTR_EMULATE_PREPARES の値を設定しないため、データベース設定ファイルで EmulatePrepare の値を false に指定してください。
そこで質問がありますが、DSN で charset が指定されている場合でも、set names
はい、保存できません。 set names
A. クライアント (PHP プログラム) が送信したエンコーディングを mysql サーバーに伝えます
B. mysql サーバーに、クライアントが必要とする結果のエンコーディングを通知します
言い換えると、データテーブルが gbk 文字セットを使用し、PHP プログラムが UTF-8 エンコーディングを使用する場合、プログラム内でエンコーディング変換を行わずに、クエリを実行する前に set names utf8 を実行して、mysql サーバーに正しくエンコードするように指示できます。 。このようにして、クエリを utf-8 エンコーディングで mysql サーバーに送信し、取得される結果も utf-8 エンコーディングになります。これにより、プログラム内の変換エンコードの問題が解消され、文字化けしたコードが生成されることはありません。
それでは、DSN で文字セットを指定する役割は何でしょうか? これは、ローカルドライバーがエスケープするときに指定された文字セットを使用することを PDO に伝えるだけです (mysql サーバーの通信文字セットを設定するわけではありません)。
一部の新しいプロジェクトが PDO を使用せず、従来の mysql_XXX 関数ライブラリを使用する理由が本当にわかりません。 PDO を正しく使用すれば、SQL インジェクションを根本的に排除することができます。各企業の技術リーダーおよび最前線の技術研究開発担当者は、この問題に注意を払い、プロジェクトの進捗と安全性の品質を向上させるために、可能な限り PDO を使用することを強くお勧めします。 。
独自の SQL インジェクション フィルタリング関数ライブラリを作成しようとしないでください (面倒で、未知の脆弱性が簡単に作成される可能性があります)。
上記はこの記事の全内容です。非常に実践的ですので、ぜひ読んでください。