SQLインジェクションは、非常に深刻なセキュリティの問題です。これは、誤解するのが非常に簡単であるためです。ユーザー入力を組み込んだクエリを作成する明白で直感的な方法は脆弱であり、それを軽減する正しい方法は、パラメーター化について知る必要があります最初にクエリとSQLインジェクション。
これを修正する明白な方法は明白な(しかし間違った)オプションをシャットダウンすることだと私には思われます:パラメーターの代わりにWHERE句でハードコードされた値を使用する受信クエリがニースで説明的なものを返すようにデータベースエンジンを修正します代わりにパラメータを使用するように指示するエラーメッセージ。管理ツールからのアドホッククエリなどが引き続き簡単に実行できるように、これには明らかにオプトアウトオプションが必要ですが、デフォルトで有効にする必要があります。
これを行うと、SQLインジェクションがほぼ一晩でシャットダウンされますが、私の知る限り、実際にこれを行うRDBMSはありません。そうでない理由はありますか?
リテラルの使用が適切なアプローチであるケースが多すぎます。
パフォーマンスの観点から、クエリにリテラルが必要な場合があります。パフォーマンスを心配するほど大きくなるバグトラッカーがあるとします。システムのバグの70%が「クローズ」、20%が「オープン」、5%が「アクティブ」、5 %は他のステータスになります。アクティブなすべてのバグを返すクエリを
SELECT *
FROM bug
WHERE status = 'active'
status
をバインド変数として渡すのではなく、 status
に渡される値に応じて異なるクエリプランが必要です-テーブルスキャンを実行してクローズされたバグを返し、status
列をインデックススキャンして返しますアクティブなローン。現在、データベースやバージョンが異なれば、バインド変数の値に応じて、同じクエリが異なるクエリプランを使用できるようにするためのアプローチが異なります。しかし、それは、クエリを再解析するか、既存のプランを新しいバインド変数値に再利用するかどうかの決定のバランスを取るために管理する必要があるかなりの量の複雑さをもたらす傾向があります。開発者にとって、この複雑さに対処することは理にかなっています。または、データがどのように見えるかについての情報がオプティマイザよりも多い場合は、別のパスを強制することは理にかなっています。
コードの複雑さの観点からは、SQLステートメントにリテラルを含めることが完全に理にかなっていることもよくあります。たとえば、Zip_code
列には5文字の郵便番号が含まれ、追加の4桁が含まれることもあります。
SELECT substr( Zip_code, 1, 5 ) Zip,
substr( Zip_code, 7, 4 ) plus_four
数値に4つの別々のパラメーターを渡すのではなく。これらは変更されるものではないため、バインド変数を作成すると、コードが読みにくくなり、パラメーターが間違った順序でバインドされてバグが発生する可能性があります。
SQLインジェクションは、信頼されていない検証されていないソースからのテキストをクエリの他の部分と連結することによってクエリが作成されるときに発生します。このようなことは文字列リテラルで最も頻繁に発生しますが、それが発生する唯一の方法ではありません。数値のクエリは、ユーザーが入力した文字列(つまり想定は数字のみを含む)を受け取り、他の素材と連結して、通常は文字列リテラルに関連付けられている引用符なしでクエリを作成します。クライアント側の検証を過度に信頼しているコードでは、フィールド名がHTMLクエリ文字列に由来するようなものになる可能性があります。 SQLクエリ文字列を調べているコードが、それがどのようにアセンブルされたかを確認する方法はありません。
重要なのは、SQLステートメントに文字列リテラルが含まれているかどうかではなく、文字列に信頼できないソースからの文字のシーケンスが含まれているかどうかであり、その検証はクエリを構築するライブラリで最も適切に処理されます。通常、C#には文字列リテラルは許可するが他の種類の文字列式は許可しないコードを書く方法はありませんが、クエリ作成クラスではなくクエリ作成クラスを使用してクエリを作成する必要があるコーディングプラクティスルールを使用できます。文字列の連結、および非リテラル文字列をクエリビルダーに渡す人は、そのようなアクションを正当化する必要があります。
SELECT count(ID)
FROM posts
WHERE deleted = false
これらの結果をフォーラムのフッターに配置する場合は、毎回falseと言うだけのダミーパラメータを追加する必要があります。または、単純なWebプログラマーがその警告を無効にする方法を調べて続行します。
これで、列挙型の例外を追加すると言うことができますが、それでも穴は再び開きます(小さいですが)。言うまでもなく、人々は最初にvarchars
を使用しないように教育する必要があります。
インジェクションの本当の問題は、クエリ文字列をプログラムで構築することです。そのためのソリューションは、ストアドプロシージャメカニズムであり、その使用または許可されたクエリのホワイトリストを強制します。
TL; DR:WHERE
句のリテラルだけでなく、allリテラルを制限する必要があります。そうしない理由により、データベースを他のシステムから切り離したままにすることができます。
まず、あなたの前提に欠陥があります。 WHERE
句のみを制限したいのですが、それだけがユーザー入力を許可できる場所ではありません。例えば、
SELECT
COUNT(CASE WHEN item_type = 'blender' THEN 1 END) as type1_count,
COUNT(CASE WHEN item_type = 'television' THEN 1 END) AS type2_count)
FROM item
これはSQLインジェクションに対しても同様に脆弱です。
SELECT
COUNT(CASE WHEN item_type = 'blender' THEN 1 END) FROM item; DROP TABLE user_info; SELECT CASE(WHEN item_type = 'blender' THEN 1 END) as type1_count,
COUNT(CASE WHEN item_type = 'television' THEN 1 END) AS type2_count)
FROM item
したがって、WHERE
句でリテラルを制限することはできません。 allリテラルを制限する必要があります。
ここで、「なぜリテラルをまったく許可するのですか?」という質問が残ります。これを覚えておいてください。リレーショナルデータベースは、他の言語で書かれたアプリケーションの下で大部分の時間使用されますが、要件を使用する必要はありませんデータベースを使用するためのアプリケーションコード。そしてここに答えがあります:コードを書くにはリテラルが必要です。他の唯一の選択肢は、すべてのコードをデータベースに依存しない言語で書くことを要求することです。そのため、それらを使用すると、データベースに「コード」(SQL)を直接書き込むことができます。これは価値あるデカップリングであり、リテラルなしでは不可能です。 (いつかリテラルなしで好きな言語で書いてみてください。これがどれほど難しいか想像できると思います。)
一般的な例として、リテラルは値の一覧/ルックアップテーブルの母集団でよく使用されます。
CREATE TABLE user_roles (role_id INTEGER, role_name VARCHAR(50));
INSERT INTO user_roles (1, 'normal');
INSERT INTO user_roles (2, 'admin');
INSERT INTO user_roles (3, 'banned');
それらがなければ、このテーブルにデータを入力するためだけに、コードを別のプログラミング言語で記述する必要があります。 SQLでこれを直接実行する機能は、貴重です。
次に、もう1つ質問を残します。なぜプログラミング言語のクライアントライブラリがそれをしないのですか?そして、ここに非常に簡単な答えがあります:彼らはデータベースパーサー全体を再実装しますデータベースのサポートされている各バージョン。どうして?あなたがすべてのリテラルを見つけたことを保証する他の方法がないからです。正規表現では不十分です。たとえば、これにはPostgreSQLの4つの個別のリテラルが含まれます。
SELECT $lit1$I'm a literal$lit1$||$lit2$I'm another literal $$ with nested string delimiters$$ $lit2$||'I''m ANOTHER literal'||$$I'm the last literal$$;
特に有効な構文はデータベースのメジャーリリース間で頻繁に変更されるため、これを実行しようとすると、メンテナンスの悪夢になります。