web-dev-qa-db-ja.com

PreparedStatementはSQLインジェクションをどのように回避または防止しますか?

PreparedStatementsはSQLインジェクションを回避/防止することを知っています。それはどうやって? PreparedStatementsを使用して構築される最終的なフォームクエリは、文字列またはその他の形式になりますか?

107
Prabhu R

SQLインジェクションの問題は、ユーザー入力がSQLステートメントの一部として使用されることです。準備されたステートメントを使用することにより、ユーザー入力をパラメーターのコンテンツとして(およびSQLコマンドの一部としてではなく)処理させることができます。

ただし、準備されたステートメントのパラメーターとしてユーザー入力を使用せず、代わりに文字列を結合してSQLコマンドを作成する場合、準備されたステートメントを使用している場合でもSQLインジェクションに対して脆弱ですです。

66
tangens

同じことを行う2つの方法を検討してください。

PreparedStatement stmt = conn.createStatement("INSERT INTO students VALUES('" + user + "')");
stmt.execute();

または

PreparedStatement stmt = conn.prepareStatement("INSERT INTO student VALUES(?)");
stmt.setString(1, user);
stmt.execute();

「ユーザー」がユーザー入力に由来し、ユーザー入力が

Robert'); DROP TABLE students; --

その後、最初のインスタンスでは、うんざりするでしょう。第二に、あなたは安全になり、リトルボビーテーブルはあなたの学校に登録されます。

181
Paul Tomblin

PreparedStatementがSQLインジェクションを防ぐ方法を理解するには、SQLクエリ実行のフェーズを理解する必要があります。

1。コンパイルフェーズ。 2.実行フェーズ。

SQLサーバーエンジンがクエリを受信するたびに、以下のフェーズを通過する必要があります。

Query Execution Phases

  1. 解析および正規化フェーズ:このフェーズでは、クエリの構文とセマンティクスがチェックされます。クエリで使用される参照テーブルと列が存在するかどうかをチェックします。また、他にも多くのタスクがありますが、詳しくは説明しません。

  2. コンパイル段階:この段階では、select、from、whereなどのクエリで使用されるキーワードは、マシンが理解できる形式に変換されます。これは、クエリが解釈され、対応するアクションが決定されるフェーズです。また、他にも多くのタスクがありますが、詳しくは説明しません。

  3. クエリ最適化計画:このフェーズでは、クエリを実行できる方法を見つけるためのディシジョンツリーが作成されます。クエリを実行できる方法の数と、クエリを実行する各方法に関連するコストを調べます。クエリの実行に最適なプランを選択します。

  4. キャッシュ:クエリ最適化プランで選択された最適なプランはキャッシュに保存されるため、次回同じクエリが来たときにパススルーする必要はありませんフェーズ1、フェーズ2、フェーズ3を再度実行します。次回クエリが入力されると、キャッシュで直接チェックされ、そこから取得されて実行されます。

  5. 実行フェーズ:このフェーズでは、提供されたクエリが実行され、データがResultSetオブジェクトとしてユーザーに返されます。

上記の手順でのPreparedStatement APIの動作

  1. PreparedStatementsは完全なSQLクエリではなく、実行時に実際のユーザー提供のデータに置き換えられるプレースホルダーを含みます。

  2. プレースホルダーを含むPreparedStatmentがSQL Serverエンジンに渡されるたびに、以下のフェーズを通過します

    1. 解析および正規化フェーズ
    2. 編集フェーズ
    3. クエリ最適化計画
    4. キャッシュ(プレースホルダーを含むコンパイル済みクエリはキャッシュに保存されます。)

更新ユーザーセットユーザー名=?およびpassword =? WHERE id =?

  1. 上記のクエリは解析され、特別な処理としてプレースホルダーでコンパイルされ、最適化されてキャッシュされます。この段階でのクエリは既にコンパイルされ、機械が理解できる形式に変換されています。したがって、キャッシュに保存されたクエリはプリコンパイルされており、プレースホルダーのみをユーザー提供のデータに置き換える必要があると言えます。

  2. これで、実行時にユーザー提供のデータが入力されると、キャッシュからプリコンパイル済みクエリが取得され、プレースホルダーがユーザー提供のデータに置き換えられます。

PrepareStatementWorking

(プレースホルダーがユーザーデータに置き換えられた後、最終クエリは再度コンパイル/解釈されず、SQL Serverエンジンはユーザーデータを純粋なデータとして扱い、解析またはコンパイルが必要なSQLではないことに注意してください繰り返しますが、それがPreparedStatementの美しさです。)

クエリが再度コンパイルフェーズを実行する必要がない場合、プレースホルダーで置き換えられたデータはすべて純粋なデータとして扱われ、SQL Serverエンジンにとって意味がなく、クエリを直接実行します。

注:解析フェーズの後のコンパイルフェーズであり、クエリ構造を理解/解釈し、それに意味のある動作を与えます。 PreparedStatementの場合、クエリは1回だけコンパイルされ、キャッシュされたコンパイル済みクエリが常にピックアップされて、ユーザーデータを置き換えて実行されます。

PreparedStatementの1回限りのコンパイル機能により、SQLインジェクション攻撃を受けません。

例で詳細な説明を取得できます: http://javabypatel.blogspot.in/2015/09/how-prepared-statement-in-Java-prevents-sql-injection.html

96
Jayesh

PreparedStatementで使用されるSQLは、ドライバーでプリコンパイルされます。その時点から、パラメーターはSQLの実行可能部分ではなく、リテラル値としてドライバーに送信されます。したがって、パラメーターを使用してSQLを挿入することはできません。 PreparedStatementsの別の有益な副作用(プリコンパイル+パラメーターのみの送信)は、ドライバーがSQLの解析とコンパイルをそれぞれ実行する必要がないため、パラメーターの値が異なる場合でも(ドライバーがPreparedStatementsをサポートしている場合)ステートメントを複数回実行するときのパフォーマンスが向上しますパラメータが変更される時間。

26
Travis Heseman

準備されたステートメントはより安全です。パラメーターを指定された型に変換します。

たとえば、stmt.setString(1, user);userパラメータを文字列に変換します。

パラメーター実行可能コマンドを含むSQL文字列を含む:準備済みステートメントを使用しても許可されないとします。

それにメタキャラクター(別名自動変換)を追加します。

これにより、より安全になります。

3
Guru R Handa

I guess文字列になります。ただし、入力パラメーターはデータベースに送信され、実際のSQLステートメントを作成する前に適切なキャスト/変換が適用されます。

例を挙げると、CAST/Conversionが機能するかどうかを試してみるかもしれません。
それが機能する場合、最終的なステートメントを作成できます。

   SELECT * From MyTable WHERE param = CAST('10; DROP TABLE Other' AS varchar(30))

数値パラメーターを受け入れるSQLステートメントで例を試してください。
次に、文字列変数(数値パラメーターとして許容される数値コンテンツを含む)を渡してみてください。エラーは発生しますか?

次に、文字列変数(数値パラメーターとして受け入れられないコンテンツを含む)を渡してみます。何が起こるかわかりますか?

3
shahkalpesh

SQLインジェクション:ユーザーがSQLステートメントの一部である可能性のある何かを入力する機会がある場合

例えば:

文字列クエリ=“ INSERT INTO Students VALUES( ’” + user +“’)”

ユーザーが「ロバート」を入力したとき;ドロップテーブルの学生; –”入力として、SQLインジェクションを引き起こします

準備された文はどのようにこれを防ぎますか?

文字列クエリ=「INSERT INTO Students VALUES( ’」+「:name」+「’)」

parameters.addValue(“ name”、user);

=>ユーザーが再度「Robert」を入力したとき);ドロップテーブルの学生; –“、入力文字列はリテラル値としてドライバーでプリコンパイルされ、次のようにキャストされると思います。

CAST( 'Robert');ドロップテーブルの学生; – ’AS varchar(30))

そのため、最後に、文字列はテーブルの名前として文字通り挿入されます。

http://blog.linguiming.com/index.php/2018/01/10/why-prepared-statement-avoids-sql-injection/

2
jack

この投稿 で説明されているように、PreparedStatementだけでは、まだ文字列を連結している場合には役に立ちません。

たとえば、1人の不正な攻撃者は引き続き次のことを実行できます。

  • すべてのデータベース接続がビジーになるようにスリープ関数を呼び出して、アプリケーションを使用不可にします
  • dBから機密データを抽出する
  • ユーザー認証のバイパス

バインドパラメータを使用していない場合、SQLだけでなく、JPQLやHQLでも危険にさらされる可能性があります。

要するに、SQLステートメントを作成するときは、文字列連結を使用しないでください。そのために専用のAPIを使用します。

1
Vlad Mihalcea

PreparedStatement:

1)SQLステートメントのプリコンパイルとDB側のキャッシュにより、全体的な実行速度が向上し、同じSQLステートメントをバッチで再利用できます。

2)引用符やその他の特殊文字の組み込みエスケープによるSQLインジェクション攻撃の自動防止。これには、いずれかのPreparedStatement setXxx()メソッドを使用して値を設定する必要があることに注意してください。

1
Mukesh Kumar