はいの場合、SQL インジェクションの成功がまだ非常に多いのはなぜですか? 一部の開発者がパラメーター化されたステートメントを使用できないという理由だけでしょうか?
12 に答える
パラメータ化されたクエリが SQL 攻撃を阻止するという記事が書かれているとき、その理由はあまり説明されていません。悪い教育者の確かな兆候は、自分が何かを知らないことを認められないことです。しかし、私は脱線します。混乱するのは完全に理解できると私が言うとき、それは簡単です。動的 SQL クエリを想像してみてください
sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND Pass=' + password
したがって、単純な SQL インジェクションは、ユーザー名を ' OR 1=1 として入力するだけです。これにより、効果的に SQL クエリが作成されます。
sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS=' + password
これは、ユーザー名が空白 ('') または 1=1 であるすべての顧客を選択することを示しています。これはブール値であり、true に相当します。次に -- を使用して、残りのクエリをコメントアウトします。したがって、これはすべての顧客テーブルを出力するか、必要なことを何でも行います。ログインする場合、最初のユーザーの権限でログインします。これは多くの場合、管理者です。
パラメータ化されたクエリは、次のようなコードを使用して、別の方法で実行します。
sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'
parameters.add("User", username)
parameters.add("Pass", password)
username と password は、関連する入力されたユーザー名とパスワードを指す変数です
この時点で、これで何も変わらないと思うかもしれません。確かに、ユーザー名フィールドに「Nobody OR 1=1'--」のようなものを入力して、効果的にクエリを作成することもできます。
sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND Pass=?'
そして、これは有効な議論のように思えます。しかし、あなたは間違っているでしょう。
パラメーター化されたクエリが機能する方法は、sqlQuery がクエリとして送信され、データベースがこのクエリの動作を正確に認識し、ユーザー名とパスワードを単に値として挿入することです。これは、データベースがクエリが何をするかを既に知っているため、クエリに影響を与えることができないことを意味します。したがって、この場合、「Nobody OR 1=1'--」というユーザー名と空白のパスワードが検索されますが、これは false になるはずです。
ただし、これは完全な解決策ではなく、JavaScript をデータベースに入れることができるため、XSS 攻撃などの他の問題には影響しないため、入力の検証を行う必要があります。次に、これがページに読み込まれると、出力の検証に応じて、通常の JavaScript として表示されます。したがって、最善の方法は入力検証を使用することですが、パラメーター化されたクエリまたはストアド プロシージャを使用して SQL 攻撃を阻止します。
質問へのコメントに投稿したリンクは、問題を非常によく説明しています。問題が解決しない理由について、私の気持ちを以下にまとめました。
始めたばかりの人は、SQL インジェクションを認識していない可能性があります。
SQL インジェクションを認識している人もいますが、エスケープが (唯一の?) 解決策であると考えています。を Google ですばやく検索する
php mysql query
と、最初に表示されるmysql_query
ページが、エスケープされたユーザー入力をクエリに挿入する例を示したページです。代わりに準備済みステートメントを使用することについては言及されていません (少なくとも私が見ることはできません)。他の人が言ったように、パラメーター補間を使用するチュートリアルは非常に多く、いまだに使用されている頻度はそれほど驚くべきことではありません。パラメータ化されたステートメントがどのように機能するかについての理解の欠如。値をエスケープする単なる派手な手段だと考える人もいます。
他の人は、パラメーター化されたステートメントを認識していますが、遅すぎると聞いたので使用しません。多くの人は、パラメータ化されたステートメントが信じられないほど遅いことを聞いたことがあると思いますが、実際に独自のテストを行ったことはありません。Bill Karwin が講演で指摘したように、準備済みステートメントの使用を検討する際に、パフォーマンスの違いを要因として使用することはほとんどありません。一度準備して何度も実行することの利点は、セキュリティとコードの保守性の向上と同様に、しばしば忘れられているように見えます。
パラメーター化されたステートメントをどこでも使用するものもありますが、テーブルや列の名前、キーワード、条件演算子などの未チェックの値を補間します。ユーザーが多数の異なる検索フィールド、比較条件、並べ替え順序を指定できるような動的検索は、この典型的な例です。
ORM 使用時の誤った安心感。ORM では SQL ステートメント部分の補間が引き続き許可されます - 5 を参照してください。
プログラミングは大きく複雑な主題であり、データベース管理は大きく複雑な主題であり、セキュリティは大きく複雑な主題です。安全なデータベース アプリケーションを開発するのは簡単ではありません。
stackoverflow に関する回答の多くは役に立ちません。動的 SQL とパラメーター補間を使用する質問を作成すると、代わりにパラメーター化されたステートメントを使用することを提案する応答が不足することがよくあります。いくつかの機会に、準備済みステートメントを使用するという私の提案に反論する人がいます。通常は、パフォーマンスのオーバーヘッドが許容できないと認識されているためです。これらの質問のほとんどを尋ねている人が、パラメーター化されたステートメントを準備するために余分にかかる数ミリ秒がアプリケーションに壊滅的な影響を与える立場にあるとは思えません。
いい質問ですね。答えは決定論的というよりも確率論的です。小さな例を使用して、私の見解を説明しようと思います。
ネット上には、SQL インジェクション (SQLi) を回避するために、クエリでパラメーターを使用するか、ストアド プロシージャをパラメーターと共に使用することを提案する参考文献が多数あります。ストアド プロシージャ (たとえば) は SQLi に対する魔法の杖ではないことを示します。責任は依然としてプログラマにあります。
テーブル 'Users' からユーザー行を取得する次の SQL Server ストアド プロシージャを検討してください。
create procedure getUser
@name varchar(20)
,@pass varchar(20)
as
declare @sql as nvarchar(512)
set @sql = 'select usrID, usrUName, usrFullName, usrRoleID '+
'from Users '+
'where usrUName = '''+@name+''' and usrPass = '''+@pass+''''
execute(@sql)
ユーザー名とパスワードをパラメーターとして渡すことで、結果を取得できます。パスワードがフリー テキストであると仮定すると (この例を簡単にするため)、通常の呼び出しは次のようになります。
DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)
EXECUTE @RC = [dbo].[getUser]
@name = 'admin'
,@pass = '!@Th1siSTheP@ssw0rd!!'
GO
しかし、ここではプログラマーがストアド プロシージャ内で使用する不適切なプログラミング手法を使用しているため、攻撃者は次のことを実行できます。
DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)
EXECUTE @RC = [TestDB].[dbo].[getUser]
@name = 'admin'
,@pass = 'any'' OR 1=1 --'
GO
上記のパラメーターは引数としてストアド プロシージャに渡され、最終的に実行される SQL コマンドは次のとおりです。
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = 'admin' and usrPass = 'any' OR 1=1 --'
..これにより、ユーザーからすべての行が返されます
ここでの問題は、「ストアド プロシージャを作成し、検索するフィールドをパラメーターとして渡す」という原則に従っても、SQLi がまだ実行されていることです。これは、ストアド プロシージャ内で不適切なプログラミング プラクティスをコピーしただけだからです。この問題の解決策は、ストアド プロシージャを次のように書き直すことです。
alter procedure getUser
@name varchar(20)
,@pass varchar(20)
as
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = @name and usrPass = @pass
私が言おうとしているのは、開発者は最初に SQLi 攻撃とは何か、どのように実行できるかを学び、それからコードを適切に保護する必要があるということです。やみくもに「ベスト プラクティス」に従うことが常に安全な方法とは限りません。
はい、準備済みステートメントを使用すると、少なくとも理論的には、すべての SQL インジェクションが停止します。実際には、パラメータ化されたステートメントは実際の準備されたステートメントではない場合があります。たとえばPDO
、PHP ではデフォルトでそれらをエミュレートするため、エッジ ケース攻撃を受けやすくなります。
実際に準備されたステートメントを使用している場合、すべてが安全です。少なくとも、たとえばテーブル名を準備できないことへの反応として、安全でない SQL をクエリに連結しない限り。
はいの場合、SQL インジェクションの成功がまだ非常に多いのはなぜですか? 一部の開発者がパラメーター化されたステートメントを使用できないという理由だけでしょうか?
はい、ここでの主なポイントは教育であり、レガシー コード ベースです。多くのチュートリアルではエスケープを使用しており、残念ながらそれらを Web から簡単に削除することはできません。
SQL インジェクションは、データとコードが同じチャネルを介して提供され、データがコードと間違われるコード インジェクションのより大きな問題のサブセットです。パラメーター化されたクエリは、データとコードに関するコンテキストを使用してクエリを形成することで、これが発生するのを防ぎます。
いくつかの特定のケースでは、これは十分ではありません。多くの DBMS では、ストアド プロシージャを使用して SQL を動的に実行できるため、DBMS レベルで SQL インジェクションの欠陥が発生します。パラメータ化されたクエリを使用してこのようなストアド プロシージャを呼び出しても、プロシージャ内の SQL インジェクションが悪用されるのを防ぐことはできません。別の例は、このブログ投稿で見ることができます。
多くの場合、開発者は機能を誤って使用します。通常、正しく実行すると、コードは次のようになります。
db.parameterize_query("select foo from bar where baz = '?'", user_input)
一部の開発者は、文字列を連結してからパラメーター化されたクエリを使用しますが、これは、探しているセキュリティ保証を提供する前述のデータ/コードの区別を実際には行いません。
db.parameterize_query("select foo from bar where baz = '" + user_input + "'")
パラメーター化されたクエリを正しく使用すると、SQL インジェクション攻撃に対する非常に強力な保護が提供されますが、完全ではありません。
プログラミングでは絶対を避けます。常に例外があります。ストアド プロシージャとコマンド オブジェクトを強くお勧めします。私のバックグラウンドの大部分は SQL Server ですが、MySql もときどき使っています。キャッシュされたクエリ プランなど、ストアド プロシージャには多くの利点があります。はい、これはパラメーターとインライン SQL で実現できますが、インジェクション攻撃の可能性が広がり、関心の分離には役立ちません。私のアプリケーションは通常、上記のストアドプロシージャの実行権限しか持っていないため、データベースを保護する方がはるかに簡単です。テーブル/ビューへの直接アクセスがなければ、何かを注入することははるかに困難です。アプリケーション ユーザーが侵害された場合、事前に定義されたものを正確に実行する権限しかありません。
私の2セント。
私は「ばか」とは言いません。
チュートリアルが問題だと思います。ほとんどのSQLチュートリアル、本、インライン値でSQLを説明しているものは何でも、バインドパラメーターについてはまったく言及していません。これらのチュートリアルから学んでいる人は、正しく学ぶ機会がありません。
アプリケーションを SQL インジェクションから保護するには、次の手順を実行します。
ステップ 1. 入力を制限します。ステップ 2. ストアード・プロシージャーでパラメーターを使用します。ステップ 3. 動的 SQL でパラメーターを使用します。
http://msdn.microsoft.com/en-us/library/ff648339.aspxを参照してください。
まず、最初の質問に対する私の答え: はい、私の知る限り、パラメーター化されたクエリを使用することで、SQL インジェクションはできなくなります。あなたの次の質問については、私には確信が持てず、その理由について私の意見を述べることしかできません。
いくつかの異なる部分 (おそらくいくつかの論理チェックに依存することさえあります) を挿入する値と一緒に連結することによって、SQL クエリ文字列を "ただ" 記述する方が簡単だと思います。クエリを作成して実行するだけです。もう 1 つの利点は、SQL クエリ文字列を印刷 (エコー、出力など) し、この文字列をデータベース エンジンへの手動クエリに使用できることです。
準備されたステートメントを使用する場合、常に少なくとも 1 つのステップがあります。 クエリを作成する必要があります (もちろん、パラメーターを含む) サーバーでクエリを準備する必要があります 必要な実際の値にパラメーターをバインドする必要がありますクエリに使用するには クエリを実行する必要があります。
特に、非常に長命であることがよくあるいくつかの「迅速で汚い」ジョブの場合は、多少手間がかかります(そして、プログラムするのはそれほど簡単ではありません...
よろしくお願いします、
箱
パラメータ化されたステートメントはすべての SQL インジェクションを停止できますか?
はい、データベース ドライバーがすべての可能な SQL リテラルのプレースホルダーを提供する限り。ほとんどのプリペアド ステートメント ドライバーはそうではありません。たとえば、フィールド名や値の配列のプレースホルダーを見つけることは決してないでしょう。これにより、開発者は、連結と手動の書式設定を使用して、手動でクエリを調整することに戻ります。予測結果付き。
そのため、配列や識別子など、クエリに動的に追加できるほとんどのリテラルをサポートする PHP 用の Mysql ラッパーを作成しました。
はいの場合、SQL インジェクションの成功がまだ非常に多いのはなぜですか? 一部の開発者がパラメーター化されたステートメントを使用できないという理由だけでしょうか?
おわかりのように、実際には、頭が悪くなくても、すべてのクエリをパラメーター化することは不可能です。
ほとんどのコードはセキュリティと管理を念頭に置いて書かれていないため、機能の追加 (特に販売可能な目に見えるもの) とセキュリティ/安定性/信頼性 (これは販売がはるかに難しい) のどちらを選択するかを考えると、ほとんどの場合、前の。セキュリティが問題になるのは、問題が発生したときだけです。