PreparedStatementsはSQLインジェクションを回避/防止することを知っています。それはどのように行うのですか?PreparedStatementsを使用して構築される最終的なフォームクエリは、文字列になるか、それともそれ以外になりますか?
PreparedStatementsはSQLインジェクションを回避/防止することを知っています。それはどのように行うのですか?PreparedStatementsを使用して構築される最終的なフォームクエリは、文字列になるか、それともそれ以外になりますか?
回答:
SQLインジェクションの問題は、ユーザー入力がSQLステートメントの一部として使用されることです。準備済みステートメントを使用することで、ユーザー入力をパラメーターのコンテンツとして(SQLコマンドの一部としてではなく)処理するように強制できます。
しかし、ユーザー入力を準備済みステートメントのパラメーターとして使用せず、代わりに文字列を結合してSQLコマンドを作成する場合、準備済みステートメントを使用する場合でも、SQLインジェクションに対して脆弱です。
同じことを行う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; --
その後、最初のインスタンスでは、あなたは夢中になります。2つ目は安全で、リトルボビーテーブルが学校に登録されます。
PreparedStatementがSQLインジェクションを防ぐ方法を理解するには、SQLクエリ実行のフェーズを理解する必要があります。
1.コンパイルフェーズ。2.実行フェーズ。
SQLサーバーエンジンがクエリを受信するたびに、以下のフェーズを通過する必要があります。
解析および正規化フェーズ: このフェーズでは、クエリの構文とセマンティクスがチェックされます。クエリで使用される参照テーブルと列が存在するかどうかをチェックします。他にも多くのタスクがありますが、詳しくは説明しません。
コンパイルフェーズ: このフェーズでは、select、from、whereなどのクエリで使用されるキーワードが、マシンが理解できる形式に変換されます。これは、クエリが解釈され、対応するアクションが決定されるフェーズです。他にも多くのタスクがありますが、詳しくは説明しません。
クエリ最適化計画: このフェーズでは、クエリを実行する方法を見つけるためのディシジョンツリーが作成されます。クエリを実行できる方法の数と、クエリを実行する各方法に関連付けられているコストがわかります。クエリを実行するための最適なプランを選択します。
キャッシュ: クエリ最適化プランで選択された最適なプランはキャッシュに保存されるため、次回同じクエリが来たときに、フェーズ1、フェーズ2、フェーズ3を再度通過する必要はありません。次回クエリが来たときに、キャッシュで直接チェックされ、そこから取得されて実行されます。
実行フェーズ:
このフェーズでは、指定されたクエリが実行され、データがResultSet
オブジェクトとしてユーザーに返されます。
PreparedStatementsは完全なSQLクエリではなく、プレースホルダーが含まれています。プレースホルダーは、実行時にユーザーが提供する実際のデータに置き換えられます。
プレースホルダーを含むPreparedStatmentがSQL Serverエンジンに渡されるたびに、以下のフェーズを通過します
UPDATEユーザーセットusername =?およびpassword =?WHERE id =?
上記のクエリは解析され、特別な扱いとしてプレースホルダーでコンパイルされ、最適化されてキャッシュされます。この段階でのクエリはすでにコンパイルされ、機械が理解できる形式に変換されています。したがって、キャッシュに格納されたクエリはプリコンパイル済みであり、プレースホルダのみをユーザー指定のデータで置き換える必要があると言えます。
現在、ユーザー提供のデータが入ってくる実行時に、事前コンパイルされたクエリがキャッシュから取得され、プレースホルダーがユーザー提供のデータに置き換えられます。
(プレースホルダーがユーザーデータに置き換えられた後、最終クエリは再度コンパイル/解釈されず、SQL Serverエンジンはユーザーデータを純粋なデータとして扱い、解析またはコンパイルが必要なSQLではないことを覚えておいてください。これがPreparedStatementの優れた点です。 )
クエリで再度コンパイルフェーズを実行する必要がない場合、プレースホルダーで置き換えられたデータはすべて純粋なデータとして扱われ、SQL Serverエンジンにとって意味がなく、直接クエリを実行します。
注:クエリ構造を理解/解釈し、意味のある動作を与えるのは、解析フェーズの後のコンパイルフェーズです。PreparedStatementの場合、クエリは1回だけコンパイルされ、キャッシュされたコンパイル済みクエリは常に取得され、ユーザーデータを置き換えて実行します。
PreparedStatementの1回限りのコンパイル機能により、SQLインジェクション攻撃がありません。
ここで例を使用して詳細な説明を取得できます:https : //javabypatel.blogspot.com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html
PreparedStatementで使用されるSQLは、ドライバーでプリコンパイルされます。その時点から、パラメーターはリテラル値としてドライバーに送信され、SQLの実行可能部分ではありません。したがって、パラメーターを使用してSQLを注入することはできません。ドライバーがそれぞれSQLの解析とコンパイルを実行する必要がないため、PreparedStatements(プリコンパイル+パラメーターのみの送信)のもう1つの有益な副作用は、パラメーターの異なる値でも(ドライバーがPreparedStatementsをサポートしていると仮定して)ステートメントを複数回実行するときのパフォーマンスの向上です。パラメータが変更される時間。
私は推測する、それは文字列になります。ただし、入力パラメーターはデータベースに送信され、実際のSQLステートメントを作成する前に、適切なキャスト/変換が適用されます。
例を挙げれば、CAST / Conversionが機能するかどうか試してみてください。
それが機能する場合、それから最終的なステートメントが作成される可能性があります。
SELECT * From MyTable WHERE param = CAST('10; DROP TABLE Other' AS varchar(30))
数値パラメーターを受け入れるSQLステートメントの例を試してください。
次に、文字列変数を渡してみてください(数値パラメーターとして受け入れ可能な数値コンテンツを使用)。エラーが発生しますか?
次に、文字列変数を渡してみます(数値パラメーターとして受け入れられない内容を含む)。何が起こるか見ますか?
SQLインジェクション:ユーザーがSQLステートメントの一部になる可能性のある何かを入力する機会がある場合
例えば:
文字列クエリ=“ INSERT INTO student VALUES( '” + user +“')”
ユーザーが「ロバート」を入力したとき。DROP TABLEの学生。–」入力として、SQLインジェクションが発生します
準備されたステートメントはこれをどのように防ぎますか?
文字列クエリ=“ INSERT INTO student VALUES( '” +“:name” +“')”
parameters.addValue( "name"、user);
=>ユーザーが再度「Robert」と入力した場合); DROP TABLEの学生。–「、入力文字列はリテラル値としてドライバーでプリコンパイルされており、次のようにキャストされると思います。
CAST( 'ロバート'); DROP TABLEの学生。– 'AS varchar(30))
したがって、最後に、文字列は文字通りテーブルの名前として挿入されます。
http://blog.linguiming.com/index.php/2018/01/10/why-prepared-statement-avoids-sql-injection/
CAST(‘Robert’);
からの部分CAST(‘Robert’); DROP TABLE students; –‘ AS varchar(30))
が壊れて、それがそうであった場合、テーブルをドロップし始めます。これは注入を停止するので、この例はシナリオを説明するのに十分なだけではないと思います。
この投稿で説明されているように、PreparedStatement
文字列をまだ連結している場合、だけでは役に立ちません。
たとえば、1人の不正な攻撃者が次のことを実行できます。
SQLだけでなく、JPQLやHQLでも、バインドパラメータを使用していない場合は危険にさらされる可能性があります。
結論として、SQLステートメントを作成するときに文字列連結を使用しないでください。そのために専用のAPIを使用します。
準備済みステートメントでは、ユーザーはデータをパラメーターとして入力する必要があります。ユーザーがDROP TABLEやSELECT * FROM USERSなどの脆弱なステートメントを入力した場合、これらはSQLステートメントのパラメーターと見なされるため、データは影響を受けません