ホームページ  >  記事  >  バックエンド開発  >  PDO の抗注射原理の解析と PDO 使用上の注意点

PDO の抗注射原理の解析と PDO 使用上の注意点

WBOY
WBOYオリジナル
2016-08-08 09:30:03849ブラウズ

PDO が合理的かつ正しく使用されている限り、基本的に SQL インジェクションを防ぐことができることは誰もが知っています。この記事は主に次の 2 つの質問に答えます:

mysql_connect の代わりに PDO を使用するのはなぜですか?

PDO はなぜアンチインジェクションなのですか?

インジェクションを防ぐために PDO を使用する場合、特に注意すべきことは何ですか?

1. なぜ最初に PDO を使用する必要があるのですか?

PHP マニュアルには次のことが明確に記載されています。より成熟したデータベースでは、プリペアド ステートメントの概念がサポートされています。プリペアド ステートメントは、アプリケーションが実行する SQL のコンパイル済みテンプレートの一種と考えることができ、変数パラメーターを使用してカスタマイズできます。 2 つの大きな利点があります:

クエリを解析 (または準備) する必要があるのは 1 回だけですが、同じまたは異なるパラメーターを使用して複数回実行できます。クエリが準備されると、データベースは実行計画を分析、コンパイル、最適化します。複雑なクエリの場合。 このプロセスには十分な時間がかかるため、異なるパラメーターを使用して同じクエリを何度も繰り返す必要がある場合、アプリケーションは分析/コンパイル/最適化の繰り返しを避けることができます。 これは、準備されたステートメントが使用するリソースが少なくなるため、ドライバーが自動的に処理する必要がなく、SQL が使用されないことを意味します。注射が発生します

(ただし、 クエリの他の部分がエスケープされていない入力で構築されている場合でも、SQL インジェクションは可能です)

PDO の prepare メソッドを使用した場合でも、主な目的は 同じ SQL テンプレート クエリのパフォーマンスを向上させ、SQL インジェクションを防ぐことです。

同時に、PHPマニュアルでは警告メッセージが表示されます

PHP 5.3.6より前では、この要素はサイレントに無視されていましたが、ドライバーでも同じ動作を部分的に再現できます。

警告

以下の例のメソッドは、ISO-8859-1 や UTF-8 など、ASCII と同じ下位 7 ビット表現を共有する文字セットでのみ使用できます。異なる表現 (UTF-16 や Big5 など) を持つ文字セットを使用するユーザーは、必ず使用する必要があります PHP 5.3.6 以降のバージョンで提供される charset
オプション

PDO::MYSQL_ATTR_INIT_COMMAND

これは、PHP 5.3.6 以前のバージョンでは、DSN の文字セット定義がサポートされていないが、使用する必要があることを意味します PDO::MYSQL_ATTR_INIT_COMMAND设置初始SQL, 即我们常用的 set names gbk指令。

いくつかのプログラムを確認し、インジェクションを防ぐためにまだ addslashes を使用しようとしていますが、使用できません実際には、さらに問題があることを知ってください。詳細については、http://www.lorui.com/addslashes-mysql_escape_string-mysql_real_eascape_string.html を参照してください。

データベース クエリを実行する前に、選択を変更する方法もあります。 、union、... このようなキーワードをクリーンアップします。このアプローチは明らかに間違った対処方法であり、提出されたテキストに学生組合が含まれている場合、置き換えによって元の内容が改ざんされ、無実の人々が無差別に殺害されることになるため、お勧めできません。

2. PDO が SQL インジェクションを防止できるのはなぜですか? まず次の PHP コードを見てください:

$pdo = new PDO("mysql:host=192.168.0.1;dbname=test;charset=utf8","root" );

$st = $pdo->prepare("select * from info where id =? and name = ?");

$id = 21;

$name = '

zhangsan ';

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

$st->bindParam(2,$name);

$st->execute () ;

$st->fetchAll();

?>

環境は以下の通り:

PHP 5.4.7

Mysqlプロトコルバージョン10

MySQL Server 5.5.27

PHPとMySQLサーバー間の通信の詳細を徹底的に理解するために、調査のために特別にWiresharkを使用してパケットをキャプチャしました。Wireshakをインストールした後、フィルター条件をtcpに設定しました。 port==3306、以下に示すように:


このように、不要な干渉を避けるために、mysql 3306 ポートとの通信データのみが表示されます。

Wireshak は wincap ドライバーに基づいており、ローカル ループバック インターフェイスでのリッスンをサポートしていないことに注意することが重要です (php を使用してローカルの mysql メソッドに接続しても、リッスンすることはできません)。他のマシン (ブリッジされたネットワークを備えた仮想マシンも MySQL でテストできます)。

次に、PHP プログラムを実行します。リスニング結果は次のとおりです。PHP は単純に SQL を MySQL Server に直接送信していることがわかりました。



実際、これは関連しています。通常、mysql_real_escape_string を使用して文字列をエスケープし、それを SQL ステートメントに結合します。違いはありません (PDO ローカル ドライバーによってのみエスケープされます)。この場合でも、SQL インジェクションが発生する可能性があります。つまり、php でローカルに呼び出すと、pdo prepare の Mysql_real_escape_string はローカルのシングルバイト文字セットを使用してクエリを操作しますが、マルチバイトでエンコードされた変数を渡すと、依然として SQL インジェクションの脆弱性 (バージョンの問題の 1 つ) が発生する可能性があります。 PHP 5.3.6 より前のバージョンでは、PDO を使用するときに php 5.3.6 以降にアップグレードし、DSN 文字列で文字セットを指定することが推奨される理由も説明されています

php 5.3.6 より前のバージョンの場合、次のコードは依然として問題を引き起こす可能性がありますSQL インジェクション。

$pdo->query('SET NAMES GBK');

$var = chr(0x27) .

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

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

$stmt->execute(array($var) ));

その理由は、上記の分析と一致しています

正しいエスケープは、mysql サーバーの文字セットを指定し、変数を MySQL サーバーに送信して文字エスケープを完了することです。次に、PHP ローカル エスケープを無効にして MySQL サーバーをエスケープするにはどうすればよいでしょうか?

PDO には、PHP を使用してローカルで準備をシミュレートするかどうかを示す

PDO::ATTR_EMULATE_PREPARES

という名前のパラメーターがあります。先ほど作成したパケット キャプチャ分析の結果によると、PHP 5.3.6 以降はローカル変数を使用して SQL に変換し、この値を false に設定して効果を試してみます。次のコードに示すように:

$pdo = new PDO("mysql:host=192.168.0.1;dbname=test;","root");$pdo- >setAttribute(PDO:: ATTR_EMULATE_PREPARES, false);

$st = $pdo->prepare("select * from info where id =? and name = ?");

$id = 21;

$name = 'zhangsan';

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

$st->bindParam(2,$name);

$st->execute();

$ st->fetchAll();

?>

以下のプログラムを実行し、Wireshark を使用してパケットをキャプチャして分析します。 結果は次のとおりです。



見ましたか?これが魔法です。今回は、PHP が SQL テンプレートと変数を 2 回に分けて MySQL に送信し、MySQL が変数のエスケープを完了することがわかります。 SQL インジェクションが問題ですが、次のように DSN で charset 属性を指定する必要があります:

$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8' , 'root' );

このようにして、SQL インジェクションの問題を根本的に解決できます。これについてよくわからない場合は、zhangxugg@163.comにメールを送って一緒に話し合うことができます。

3. PDO を使用する際の注意事項

上記の点を理解した後、SQL インジェクションを防ぐために PDO を使用するためのいくつかの注意事項をまとめることができます:

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

2. PHP 5.3.6以降を使用している場合は、PDO3のDSNでcharset属性を指定してください。 PHP 5.3.6以前のバージョンを使用している場合は、PDO::ATTR_EMULATE_PREPARESパラメータを設定してください。が false (つまり、変数処理が MySQL によって実行される) の場合、PHP 5.3.6 以降では、ローカル シミュレーションの prepare を使用するか、mysql サーバーの prepare を呼び出すかに関係なく、この問題はすでに処理されています。 DSN での文字セットの指定は無効であり、セット名 の実行が必須です。

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

そこで、DSN で charset が指定されている場合でも、set names を実行する必要がありますか?

はい、保存できません。 set names には、実際には 2 つの機能があります:

A. クライアント (PHP プログラム) が送信したエンコーディングを mysql サーバーに伝えます

B。クライアントが必要とする結果のエンコーディングは何ですか? つまり、データ テーブルが gbk 文字セットを使用し、PHP プログラムが UTF-8 エンコーディングを使用する場合、クエリを実行する前に set names utf8 を実行し、Mysql サーバーに必要なものだけを伝えます。正しくエンコードするため、プログラム内でエンコードを変換する必要はありません。このようにして、クエリを utf-8 エンコーディングで mysql サーバーに送信し、取得される結果も utf-8 エンコーディングになります。これにより、プログラム内の変換エンコードの問題が解消され、文字化けしたコードが生成されることはありません。

それでは、DSN で文字セットを指定する役割は何でしょうか? これは、ローカルドライバーがエスケープするときに指定された文字セットを使用することを PDO に伝えるだけであり (mysql サーバーの通信文字セットは設定しません)、mysql サーバーを設定します。通信文字セットには set names コマンドも使用する必要があります。

写真が失われた場合は、zhangxugg@163.com
に電子メールを送信して、PDF バージョンをリクエストできます。

一部の新しいプロジェクトが PDO を使用せず、従来の mysql_XXX 関数ライブラリを使用する理由が本当にわかりません。 PDO を正しく使用すれば、SQL インジェクションを根本的に排除することができます。各企業の技術リーダーおよび最前線の技術研究開発担当者は、この問題に注意を払い、プロジェクトの進捗と安全性の品質を向上させるために、可能な限り PDO を使用することを強くお勧めします。 。 独自の SQL インジェクション フィルタリング関数ライブラリを作成しようとしないでください (面倒で、未知の脆弱性が簡単に作成される可能性があります)。

以上、PDO のアンチインジェクション原理の分析と、PDO を使用する際の注意事項をあらゆる側面を含めて紹介しました。PHP チュートリアルに興味のある友人の参考になれば幸いです。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。