98


42

PreparedStatementsはSQLインジェクションを回避/防止します。 それはどのようにしますか? PreparedStatementsを使用して構築された最終的なフォームクエリは文字列になりますか、それ以外のものになりますか?

9 Answer


169


同じことをする2つの方法を考えてください。

PreparedStatement stmt = conn.createStatement( "学生に値を挿入します( '" user "')"); stmt.execute();

または

PreparedStatement stmt = conn.prepareStatement( "学生の値に挿入(?)"); stmt.setString(1、user); stmt.execute();

「ユーザー」がユーザー入力から来たもので、ユーザー入力が

Robert '); DROP TABLEの学生 -

それから最初の例では、あなたは嫌われるでしょう。 第二に、あなたは安全だとリトルボビーテーブルはあなたの学校のために登録されるでしょう。


80


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

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

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

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

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

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

  4. *キャッシュ:*クエリ最適化プランで選択された最適なプランは キャッシュ。これにより、次回同じクエリが入力されるたびに、フェーズ1、フェーズ2、フェーズ3を再度通過する必要がなくなります。 次回クエリが入ったとき、それはキャッシュで直接チェックされ、そこで実行されるでしょう。

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

上記の手順におけるPreparedStatement APIの動作

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

  2. プレースホルダーを含むPreparedStatmentが渡されるときはいつでも SQL Serverエンジン、以下のフェーズを通過します

  3. 解析および正規化フェーズ

  4. コンパイルフェーズ

  5. クエリ最適化計画

  6. キャッシュ(プレースホルダーを含むコンパイル済みクエリはキャッシュに保存されます。)

_ UPDATE user set username =? とパスワード=? どこid =? _

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

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

PrepareStatementWorking

(プレースホルダがユーザーデータに置き換えられた後、最後のクエリは再度コンパイル/解釈されず、SQL Serverエンジンはユーザーデータを純粋なデータとして扱い、再度解析またはコンパイルする必要があるSQLとしては扱いません。これはPreparedStatementの長所です。 。)

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

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

PreparedStatementの1回限りのコンパイル機能により、SQLインジェクション攻撃から解放されます。

あなたはここで例で詳細な説明を得ることができます:http://javabypatel.blogspot.in/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html


62


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

しかし、ユーザー入力をプリペアドステートメントのパラメーターとして使用せず、代わりにストリングを結合してSQLコマンドを作成した場合、プリペアドステートメントを使用しても* SQLインジェクションに対して*脆弱です。


26


PreparedStatementで使用されているSQLは、ドライバ上でプリコンパイルされています。 それ以降、パラメータはリテラル値としてドライバに送信され、SQLの実行可能部分には送信されません。したがって、パラメータを使用してSQLを注入することはできません。 PreparedStatements(パラメータのみを送信するプリコンパイル)のもう1つの利点は、ドライバが毎回SQL解析とコンパイルを実行する必要がないため、パラメータに異なる値を指定してもステートメントを複数回実行するときのパフォーマンスが向上することですパラメータが変わります。


3


私はそれが文字列になると思います。 しかし、入力パラメータはデータベースに送信されます

例を挙げれば、CAST / Conversionが機能するかどうかを試してみることになります。 それがうまくいけば、それはそれから最終的なステートメントを作成することができます。

SELECT * MyTableからWHERE param = CAST('10; DROP TABLEその他 'AS varchar(30))

数値パラメータを受け付けるSQL文の例を試してください。 それでは、文字列変数を渡してみます(数値パラメータとして受け入れ可能な数値コンテンツ)。 エラーが発生しますか?

それでは、文字列変数を渡してみます(数値パラメータとしては受け入れられない内容の) 何が起こるか見て?


3


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

例えば ​​`stmt.setString(1、user);`は `user`パラメータを文字列に変換します。

パラメータ*に実行可能コマンド*を含むSQL文字列が含まれているとします。

それはメタキャラクター(a.k.aを追加します。 それに自動変換)。

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


2


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

例えば:

文字列query =“ INSERT INTO学生の値(““ユーザー““)”)

ユーザーが「Robert」を入力したとき。 DROP TABLEの学生 - 入力として、それはSQLインジェクションを引き起こします

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

文字列query =“ INSERT INTO Students VALUES( "" ":name" "") "

parameters.addValue(“ name”、user);

⇒ ユーザーがもう一度「Robert」を入力したとき。 DROP TABLEの学生 - 「、入力 stringはドライバ上でリテラル値としてプリコンパイルされているので、キャストすると次のようになります。

CAST( "Robert") DROP TABLEの学生 - 「AS varchar(30))

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


1


PreparedStatement:

1)SQL文のプリコンパイルとDB側でのキャッシュにより、全体的な実行速度が速くなり、同じSQL文をバッチで再利用することができます。

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


1


https://vladmihalcea.com/a-beginners-guide-to-sql-injection-and-how-you-should-prevent-it/ [この投稿]で説明されているように、 `PreparedStatement`だけでは役に立ちません。あなたはまだ文字列を連結しています。

たとえば、1人の不正な攻撃者がまだ次のことを行うことができます。

  • スリープ関数を呼び出して、すべてのデータベース接続が 忙しいため、アプリケーションを使用できません

  • DBから機密データを抽出する

  • bypassing ユーザー認証

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

つまり、SQL文を構築するときに文字列連結を使用しないでください。 そのために専用のAPIを使用してください。