プリコンパイルによって SQL インジェクションが防止されるのはなぜですか? ひと目見ればわかります。プリコンパイル原理の詳細な説明

「著者のホームページ」:Shibie Sanri wyx
「著者について」: CSDNトップ100、Alibaba Cloudブログ専門家、Huawei Cloud Sharing専門家、ネットワークセキュリティ分野の質の高いクリエイター
「おすすめコラム」:ネットワークセキュリティに興味のある友人は、「ネットワーク セキュリティの初心者からマスターまで」のコラムに従ってください。

まず、SQL インジェクションのプロセスを簡単に理解しましょう。

たとえば、クエリ関数は、ユーザーが入力した ID に基づいてユーザー名とパスワードをクエリします。

ここに画像の説明を挿入します

バックグラウンドのSQL文はこんな感じです

select *from user where id='1'

パラメータでペイロードを送信すると

 https://127.0.0.1/Less-1/?id=-1' union select 1, 2, user()-- a

バックグラウンド SQL はこのように接続されます (ここで重要な点: SQL の構文構造が変更されています)

select *from user where id='-1' union select 1, 2, user()-- a'

SQL インジェクションの原因となったデータベースの管理者アカウントの検索にご協力ください。

ここに画像の説明を挿入します


インジェクションのプロセスから、SQL インジェクションの核心は、ユーザーが入力したパラメーターが SQL の構文構造を変更することであることがわかります。

プリコンパイルにより、構文構造の変更を防ぐことができます。プリコンパイルについて話す前に、まず SQL の実行プロセスを理解する必要があります。


1. SQL実行処理

MySQL を例に挙げると、データベースが SQL ステートメントを実行するときは、次の 7 つのステップを経る必要があります。

  1. 字句解析: SQL ステートメントをトークン (キーワード、識別子、演算子) に分解し、トークンを分類および解析して、対応するデータ構造を生成します。
  2. 構文解析: SQL 構文検出ルールに従って構文が正しいかどうかを確認し、構文ツリーを作成します。
  3. セマンティック分析: 構文ツリーをたどって、テーブルや列などの情報を特定し、セマンティクスの正確さをチェックします。
  4. 最適化処理: オプティマイザーを使用して、実行プランやインデックスなどの SQL ステートメントを処理および最適化します。
  5. 実行計画: 実行計画ジェネレーターを使用して、データへのアクセス方法、インデックスの使用方法などの SQL ステートメントの実行計画を生成します。
  6. エンジンの実行: 実行プランを対応するデータベース エンジンに送信して処理します。実行プランは、データ スキャン、インデックス検索、並べ替え、グループ化などの操作を実行するための基礎となる操作命令に変換されます。
  7. 戻りデータ: クエリ結果セットや操作結果などの実行結果をクライアントに返します。

ここでは、実行プロセスを大まかに 2 つのステップに分けて理解します。まず SQL 構文構造をコンパイルし ( 1~3步)、次に SQL 文を実行します ( 4~7步)。

通常の状況では、ユーザーが入力したパラメーターは SQL 構文のコンパイルに直接関与しますが、プリコンパイルではまず構文ツリーが構築され、SQL 構文構造が決定され、次にユーザーのパラメーターが結合されます。

2. プリコンパイルの原理

プリコンパイルの本来の目的は、コードの再利用性を向上させることです。次のような、パラメーター値が異なるだけの SQL (まったく同じ SQL がキャッシュから検索されます) が多数あるためです。

select * from user where id='1'
select * from user where id='2'

これらの SQL の構文ツリーは同じですが、毎回繰り返しコンパイルする必要があり、時間の無駄です。

プリコンパイルでは、SQL ステートメントをテンプレート化し、値の位置をプレースホルダーに置き換えることができます。このようにして、データベースは事前に SQL 構文構造をコンパイルし、実際に呼び出されたときに実行用の値を渡すため、繰り返し実行する必要がなくなります。構文ツリーを構築します。

select * from user where id={占位符}

パケット キャプチャから判断すると、最初に SQL ステートメントがプリコンパイルされ (Prepare Statement)、パラメーター値が最初にプレースホルダーに置き換えられます。実行時(Execute Statement)にパラメータを渡します。

ここに画像の説明を挿入します

ユーザーによって渡されたパラメータは構文ツリーの構築には関与しないため、SQL の構文構造を変更することができず、インジェクションを回避できます。

拡張機能:

PHP の PDO (PHP Data Object) は、複数のデータベースを操作するための統合インターフェイスであり、ローカル プリコンパイルとシミュレートされたプリコンパイルの 2 つのプリコンパイル メカニズムを提供します。
ローカル プリコンパイルとは、データベース自体のプリコンパイルを指します。これは、ここで説明したプリコンパイル方法でもあります。
シミュレートされたプリコンパイルは、プリコンパイルをサポートしていないデータベースに使用されます。基本的に、ユーザーの入力は最下位レベルで変換され、SQL ステートメントが結合され、完全な SQL ステートメントが実行のためにデータベースに送信されます。
変換されたパラメータは文字列としてのみ扱われ、SQL コンパイルには参加できません (PHP 5.3.6 より前では、シングルバイト文字セット変換が使用され、シングルバイト インジェクションが存在しました) 文字セットを正しく設定すると、SQL インジェクションを防ぐこともできます。


3. SQL インジェクションを防ぐためのプリコンパイル

MyBatis (半自動永続化レイヤー フレームワーク) を例にとると、#{id}この形式でパラメーターを渡す場合、SQL はまずプリコンパイルのためにデータベースに渡され、呼び出されると、プレースホルダーがパラメーターに置き換えられて実行されます。

<select id="getUser" resultType="Blog" parameterType=int>
         SELECT *
         FROM user
		 WHERE id=#{
    
    id}
</select>

ただし、一部の SQL では動的なテーブル名と列名の使用が必要です。この場合、プリコンパイルは使用できないため、置き換える必要があります。この方法では、パラメータは SQL コンパイルに直接関与するため、SQL インジェクションを防ぐことはできませ#{id}${id}。 、パラメータを手動でフィルタリングする必要があります。

ヒント: MyBatis フレームワークのプリコンパイルは JDBC の PreparedStatement クラスであり、そのオブジェクトにはコンパイルされた SQL ステートメントが含まれています。

PHP で MySQL のプリコンパイル機能を使用する:

1) プリコンパイルされた SQL ステートメントを定義します。パラメーターはプレースホルダーで表されますか?

$sql = "SELECT * FROM user WHERE id= ? ";

2) 前処理オブジェクトの作成

$mysqli_stmt = $mysqli->prepare($sql);

3) バインドパラメータ

$mysqli_stmt->bind_param('i', $id);

4) 結果セットをバインドする

$mysqli_stmt->bind_result($username);

5) 実行

$mysqli_stmt->execute();

4. プリコンパイルの制限

プリコンパイルのメカニズムでは、最初にコンパイルしてから値を渡しますが、ユーザーが渡したパラメーターは SQL 構文構造を変更できないため、SQL インジェクションの問題は根本的に解決されます。

ただし、動的なテーブル名と列名のシナリオなど、すべてのパラメーターでプリコンパイルを使用できるわけではありません。セマンティック分析中に、構文ツリーが解析されてテーブル名と列名が存在するかどうかが確認されるため、テーブル名と列名は使用できません。プリコンパイルを使用する方法はありません。

同様に、ソート シナリオの ASC/DESC も動的なパラメータ転送を必要とし、プリコンパイルは使用できません。

おすすめ

転載: blog.csdn.net/wangyuxiang946/article/details/132356363