単一引用符をエスケープし、単一引用符でユーザー入力を囲むことで、SQLインジェクションから保護できますか?


139

ユーザー入力を含むクエリを作成する場合、パラメーター化されたSQLクエリがユーザー入力をサニタイズするための最適な方法であることを理解していますが、ユーザー入力を取得して一重引用符をエスケープし、文字列全体を一重引用符で囲むと何が問題になるのでしょうか。これがコードです:

sSanitizedInput = "'" & Replace(sInput, "'", "''") & "'"

ユーザーが入力する一重引用符はすべて二重一重引用符に置き換えられます。これにより、ユーザーは文字列を終了できなくなります。そのため、セミコロンやパーセント記号など、ユーザーが入力する可能性のある他のすべてはすべて文字列の一部となり、コマンドの一部として実際には実行されません。

私たちはMicrosoft SQL Server 2000を使用しています。これは、単一引用符が唯一の文字列区切り文字であり、文字列区切り文字をエスケープする唯一の方法であると私は考えているため、ユーザーが入力するものを実行する方法はありません。

これに対してSQLインジェクション攻撃を仕掛ける方法は見当たらないが、もしこれが私のように弾丸であるとすれば、他の誰かがすでにそれを考えていて、それが一般的な慣行になるだろう。

このコードの何が問題になっていますか?このサニタイズ手法を超えてSQLインジェクション攻撃を取得する方法はありますか?この手法を利用するユーザー入力のサンプルは非常に役立ちます。


更新:

このコードに対してSQLインジェクション攻撃を効果的に開始する方法はまだわかりません。一部の人々は、バックスラッシュが1つの単一引用符をエスケープし、もう1つを残して文字列を終了し、残りの文字列がSQLコマンドの一部として実行されるようにすることを提案しました。 MySQLデータベースですが、SQL Server 2000では、単一引用符をエスケープする唯一の方法(私が見つけたもの)は、別の単一引用符を使用することです。バックスラッシュはそれをしません。

また、単一引用符のエスケープを停止する方法がない限り、ユーザー入力の残りの部分はすべて1つの連続した文字列と見なされるため、実行されません。

入力をサニタイズするより良い方法があることを理解していますが、上記で提供した方法が機能しない理由を学ぶことに本当に興味があります。このサニタイズ方法に対するSQLインジェクション攻撃を実装する特定の方法を誰かが知っている場合は、ぜひご覧ください。


17
@BryanH一般に受け入れられている知恵が特定のケースにどのように適用されるかを理解しないことを認め、そのような特定のケースについての例を求めることは傲慢ではなく、謙虚です。一方で、一般に受け入れられている知恵がなぜ正しいのかという例を誰かが尋ねたときにイライラすることは、傲慢であると思われるかもしれません。特定の例による推論は、多くの場合、調査および学習するための優れた方法です。OPがこの疑問をどのように処理したかは、特に彼が見つけた答えを説明するときに、私の主題の理解に非常に役立ちました。
SantiBailors 2015年

@patrik同じコードに取り組んでいるが、文字列をエスケープしてクエリをネストしようとしているため、これに遭遇しました。あなたはそれを理解したことがありますか?
3therk1ll

1
@ 3therk1ll試さないのが最善です。パラメータ化されたSQLを使用したほうがよいです。blog.codinghorror.com
Patrick

@パトリック、私は攻撃者の観点から近づいています!
3therk1ll

回答:


87

まず第一に、それは単に悪い習慣です。入力の検証は常に必要ですが、常に不明瞭です。
さらに悪いことに、ブラックリストの検証は常に問題があり、受け入れる値/形式を明示的かつ厳密に定義する方がはるかに優れています。確かに、これは常に可能とは限りませんが、ある程度は常に実行する必要があります。
このテーマに関するいくつかの研究論文:

重要なのは、あなたが行うどんなブラックリスト(そしてあまりに許容度の高いホワイトリスト)もバイパスすることができるということです。私の論文への最後のリンクは、引用のエスケープでさえ回避できる状況を示しています。

これらの状況が当てはまらない場合でも、悪い考えです。さらに、アプリがささいに小さい場合を除いて、メンテナンスとおそらくはある程度のガバナンスに対処する必要があります。どのようにして、いつでもどこでも正しく機能することを確認するにはどうすればよいでしょうか。

それを行う適切な方法:

  • ホワイトリストの検証:タイプ、長さ、形式、または受け入れられる値
  • ブラックリストを作成する場合は、すぐに行ってください。引用のエスケープは適切ですが、他の緩和策の範囲内です。
  • コマンドオブジェクトとパラメーターオブジェクトを使用して、事前解析と検証を行う
  • パラメータ化されたクエリのみを呼び出します。
  • さらに良いのは、ストアドプロシージャのみを使用することです。
  • 動的SQLの使用を避け、文字列連結を使用してクエリを作成しないでください。
  • SPを使用している場合は、データベースの権限を制限して、必要なSPのみを実行し、テーブルに直接アクセスすることはできません。
  • コードベース全体がSPを介してのみDBにアクセスすることも簡単に確認できます...

2
正しく使用すると、動的SQLと文字列連結をパラメーター化されたクエリで安全に使用できます(つまりのsp_executesql代わりにEXEC)。つまり、連結されたテキストがユーザーからのものでない限り、SQLステートメントを動的に生成できます。これにはパフォーマンス上の利点もあります。sp_executesqlキャッシングをサポートします。
ブライアン

2
@ブライアン、まあまあ:) しかし実際には、プログラマがそれを行う頻度はどれくらいですか?さらに、動的SQLが「必要」である典型的なシナリオでは、クエリの一部として(おそらく)ユーザー入力が必要です。sp_executesqlを実行できれば、(通常は)動的SQLは最初から必要ありません。
AviD 2010

ようやく、Unicodeを使用して文字列の置換をこっそりこなしている可能性があることに気づかされる状況に遭遇しました。入力テキストがWordに入力されたため、アポストロフィはストレートダウンバージョンから「カーリー」アポストロフィ(カンマのように見える)に変更されました。これは、文字列置換の影響を受けませんが、SQLでは文字列区切り文字として扱われました。サーバ。回答AviD(および他のすべての人)に感謝します!
Patrick

1
@ElRonnoco確かに、私あなたが思っているよりもずっと野生でそれを見たので、私はそれを割引しません ...
AviD

1
@AviDあなたが書いたSQL Smuggling PDFへのリンクを、オンラインで見つけた唯一のバージョンに更新しました...論文の別の場所があるかどうかお知らせください。
Michael Fredrickson、2014

41

さて、この応答は質問の更新に関連します:

「もし誰かがこのサニタイズ手法に対してSQLインジェクション攻撃を実装するための特定の方法を知っているなら、ぜひ見たいと思います。」

ここで、MySQLのバックスラッシュエスケープに加えて、実際にMSSQLについて話していることを考慮に入れると、SQLをコードに挿入する方法は3つあります。

sSanitizedInput = "'"&Replace(sInput、 "'"、 "''")& "'"

これらは常にすべてが有効であるわけではなく、その周りの実際のコードに大きく依存していることを考慮に入れてください。

  1. 2次SQLインジェクション-SQLクエリがエスケープにデータベースから取得されたデータに基づいて再構築される場合、データはエスケープされずに連結され、間接的にSQLインジェクションされる可能性があります。見る
  2. 文字列の切り捨て-(もう少し複雑)-シナリオは、ユーザー名とパスワードの2つのフィールドがあり、SQLがそれらの両方を連結する場合です。また、両方のフィールド(または最初のフィールドのみ)には、長さに厳しい制限があります。たとえば、ユーザー名は20文字に制限されています。次のコードがあるとします。
username = left(Replace(sInput, "'", "''"), 20)

次に得られるのは、ユーザー名で、エスケープされ、20文字にトリミングされます。ここでの問題-私は20番目の文字(たとえば19 aの後)に引用符を貼り付けます。エスケープする引用符は(21番目の文字で)削除されます。次に、SQL

sSQL = "select * from USERS where username = '" + username + "'  and password = '" + password + "'"

前述の不正なユーザー名と組み合わせると、パスワードはすでに引用符の外にあり、ペイロードが直接含まれます。
3. Unicode密輸-特定の状況では、引用符のように見える高レベルのUnicode文字を渡すことは可能ですが、そうではありません -突然、データベースに到達するまでです。検証すると見積もりではないので、簡単に確認できます...詳細については以前の回答を参照し、元の調査へのリンクを参照してください。


28

簡単に言えば、クエリをエスケープしないでください。あなたは何かがおかしくなります。代わりに、パラメータ化されたクエリを使用するか、何らかの理由でそれができない場合は、これを行う既存のライブラリを使用してください。自分でやる理由はありません。


2
「Google Fusion Tables」のようなものに対処する必要がある場合、その方言をサポートする利用可能な抽象化ライブラリがないため、何を提案しますか?
systempuntoout 2012

20

質問されてから久しぶりですが……

「引数の引用」手続きに対して攻撃を仕掛ける1つの方法は、文字列の切り捨てです。MSDNによると、SQL Server 2000 SP4(およびSQL Server 2005 SP1)では、長すぎる文字列は静かに切り捨てられます。

文字列を引用すると、文字列のサイズが大きくなります。すべてのアポストロフィが繰り返されます。これを使用して、SQLの一部をバッファーの外にプッシュできます。したがって、where句の一部を効果的に削除できます。

これはおそらく、「update」ステートメントを悪用して、想定されていたすべてのチェックを実行できない「ユーザー管理」ページのシナリオでほとんど役に立ちます。

したがって、すべての引数を引用することにした場合は、文字列のサイズがどうなっているのかを確認し、切り捨てに遭わないように注意してください。

パラメータを使用することをお勧めします。常に。データベースでそれを強制できればいいのに。また、副作用として、より多くのステートメントが同じように見えるため、キャッシュヒットが向上する可能性が高くなります。(これは確かにOracle 8にも当てはまりました)


1
投稿後、私はAviDの投稿がこれを詳細にカバーすることを決めました。うまくいけば、私の投稿はまだ誰かの助けになるでしょう。
ジョーン・イェンセン

10

私は「高度な検索」機能を処理するときにこの手法を使用しました。最初からクエリを作成することが唯一の実行可能な答えでした。(例:ユーザーが製品属性の無制限の制約に基づいて製品を検索できるようにし、列とその許容値をGUIコントロールとして表示して、ユーザーの学習しきい値を削減します。)

それ自体は安全なAFAIKです。ただし、別の回答者が指摘したように、バックスペースエスケープに対処する必要がある場合もあります(ただし、少なくともADOまたはADO.NETを使用してSQL Serverにクエリを渡す場合は除きます。すべてのデータベースまたはテクノロジを保証することはできません)。

問題は、どの文字列にユーザー入力(常に潜在的に悪意がある)が含まれているか、どの文字列が有効なSQLクエリであるかを本当に確認する必要があることです。トラップの1つは、データベースからの値を使用する場合です-それらの値は元々ユーザー指定でしたか?その場合は、それらもエスケープする必要があります。私の答えは、SQLクエリを構築するときに、できるだけ遅く(しかし後ではなく)サニタイズを試みることです。

ただし、ほとんどの場合、パラメーターバインディングが適しています。


2
独自のクエリを作成している場合でも、パラメーターの置換を使用できます。
ニックジョンソン

1
SQLステートメント文字列を最初から構築する必要がありますが、それでもパラメーター置換を使用してください。
JeeBee 2008

いいえ、SQLステートメントを最初から作成しないでください。
AviD 2008

8

入力衛生はあなたが半ばやりたいものではありません。お尻全体を使用してください。テキストフィールドで正規表現を使用します。数値を適切な数値型にTryCastし、機能しない場合は検証エラーを報告します。'-などの入力で攻撃パターンを検索するのは非常に簡単です。ユーザーからのすべての入力が敵対的であると仮定します。


4
そしてあなたがそれを逃したとき ONEのケースONEあなたがしているPWND、入力を。
BryanH 2012年

4
「問題に直面したとき、一部の人々は「わかっている、私は正規表現を使用するだろう」と思う。今では彼らは2つの問題を抱えている。」
MickeyfAgain_BeforeExitOfSO 2013

1
@mickeyfこれは一般的な感情だと知っていますが、正直に言うと正規表現はgrepを実行するとかなり素晴らしいものになります。
tom.dietrich

@ tom.dietrichそれは常に実際の状況に依存します。F.ex. regexpr構文は標準ではないので、一般に、異なるシステムが統合されて連携するコンテキストではregexprを使用しないことをお勧めします。これは、異なるregexprエンジンがregexprを異なる方法で評価するためであり、さらに重要なことに、この難しい事実は通常軽視されるか無視され、開発者はこれらの非互換性が噛まれるまで気にしない可能性があります。そのような非互換性はたくさんあります。f.exを参照してください。regular-expressions.info/shorthand.htmlflavorsそのページで検索)。
SantiBailors

6

あなたが知っているように、とにかくそれは悪い考えです。

次のように文字列で引用符をエスケープするようなものはどうですか:\ '

置き換えると、次のようになります:\ ''

バックスラッシュが最初の引用符をエスケープする場合、2番目の引用符が文字列を終了しています。


3
ご返信ありがとうございます!mySQLデータベースで攻撃が機能することはわかっていますが、MS SQL Serverがエスケープ文字としてバックスラッシュを受け入れないことは確かです(私は試しました)。いくつかのグーグル検索は他のエスケープ文字を明らかにしませんでした、それは本当にこれがなぜうまくいかないのか疑問に思いました。
Patrick

6

簡単な答え:時々動作しますが、常に動作するとは限りません。あなたはすべてのことに対してホワイトリスト検証を使用したいのですが、それが常に可能であるとは限らないので、最良の推測のブラックリストを使用することを強いられます。同様に、パラメーター化されたストアドプロシージャをすべてでプロシージャを使用する必要がありますが、これは常に可能であるとは限らないため、パラメーターを指定してsp_executeを使用する必要があります。

考えられる使用可能なブラックリスト(およびいくつかのホワイトリスト)を回避する方法はいくつかあります。

まともな記事はこちら:http ://www.owasp.org/index.php/Top_10_2007-A2

これを実際の場所に配置する時間を与えるためのクイックフィックスとしてこれを行う必要がある場合は、実行してください。しかし、あなたが安全だとは思わないでください。


6

SQLインジェクションから保護するために、例外なく2つの方法があります。準備されたステートメントまたはパラメータ化されたストアドプロシージャ。


4

パラメータ化されたクエリが利用可能な場合は、常にそれらを使用する必要があります。必要なのは、1つのクエリがネットをすり抜けて、DBが危険にさらされていることだけです。


4

ええ、それは誰かがSET QUOTED_IDENTIFIER OFFを実行して二重引用符を使用するまで、すぐに機能するはずです。

編集:悪意のあるユーザーが引用された識別子をオフにできないようにするほど簡単ではありません。

SQL Server Native Client ODBCドライバーおよびSQL Server Native Client OLE DB Provider for SQL Serverは、接続時にQUOTED_IDENTIFIERを自動的にONに設定します。これは、ODBCデータソース、ODBC接続属性、またはOLE DB接続プロパティで構成できます。DB-Libraryアプリケーションからの接続では、SET QUOTED_IDENTIFIERのデフォルトはOFFです。

ストアドプロシージャが作成されると、SET QUOTED_IDENTIFIERおよびSET ANSI_NULLS設定がキャプチャされ、そのストアドプロシージャの後続の呼び出しに使用されます

SET QUOTED_IDENTIFIER は、ALTER DATABASEのQUOTED_IDENTIFER設定に対応しています。

SET QUOTED_IDENTIFIERは解析時に設定されます。解析時に設定するということは、SETステートメントがバッチまたはストアドプロシージャに存在する場合、コード実行が実際にそのポイントに到達したかどうかに関係なく、SETステートメントが有効になることを意味します。SETステートメントは、ステートメントが実行される前に有効になります。

必ずしも知らないうちにQUOTED_IDENTIFIERをオフにする方法はたくさんあります。確かに-これはあなたが探している喫煙銃のエクスプロイトではありませんが、かなり大きな攻撃対象となります。もちろん、二重引用符もエスケープした場合は、元の場所に戻ります。;)


1
これでうまくいくかもしれませんが、すべてのユーザー入力が単一引用符で囲まれている場合、どのようにしてそのコードを実行できますか?上記のコードにSQLを挿入できるコードの特定の行は非常に役立ちます。ありがとう!
Patrick

4

次の場合、防御は失敗します。

  • クエリは文字列ではなく数値を期待しています
  • 以下を含む、単一引用符を表す他の方法がありました。
    • \ 039などのエスケープシーケンス
    • Unicode文字

(後者の場合は、置換を行った後にのみ拡張されたものでなければなりません)


4

パトリック、すべての入力、数値の入力も含めて一重引用符を追加していますか?数値入力があっても、その周りに一重引用符を付けていない場合は、問題があります。


1

ユーザー入力のサニタイズをすべてコード化するのは醜いことです!次に、SQLステートメントの不格好なStringBuilderです。準備されたステートメントメソッドにより、コードがよりクリーンになり、SQLインジェクションの利点は本当に素晴らしい追加です。

また、なぜホイールを再発明するのですか?


1

単一引用符を2つの単一引用符に(どのように見えるか)変更するのではなく、単にアポストロフィ、引用符に変更するか、完全に削除しないのはなぜですか?

いずれにせよ、それは少々厄介です...特に、シングルクォートを使用する可能性のあるもの(名前など)を正当に持っている場合は...

注:この方法では、アプリで作業するすべての人が、データベースに到達する前に入力をサニタイズすることを常に覚えていることも前提としています。これは、ほとんどの場合現実的ではありません。


回答が質問に対応していないため、反対票が投じられました。質問は、SQLで文字列をエスケープすることです。任意の文字列をエスケープするとき(質問者が無害化されたデータを処理するためにそうしようとしているように)、問題のある文字を他の任意の文字で置き換えることはできません。データが破損します。(また、単一引用符はアポストロフィ(少なくともASCII)です)
andrewf

-1

文字列で機能するソリューションが見つかるかもしれませんが、数値述語については、それらが数値でのみ渡されることを確認する必要もあります(簡単なチェックは、int / double / decimalとして解析できるかどうかです)。

それは多くの余分な作業です。


-2

うまくいくかもしれませんが、私には少し面白そうです。代わりに、正規表現に対してテストして、各文字列が有効であることを確認することをお勧めします。


-3

はい、できます...

トピックを調査した後、あなたが提案したようにサニタイズされた入力は安全ですが、これらのルールの下でのみです:

  1. ユーザーからの文字列値が文字列リテラル以外になることを決して許可しません(つまり、「追加のSQL列名/式をここに入力してください:」という構成オプションを指定しないでください)。文字列以外の値の型(数値、日付など):それらをネイティブデータ型に変換し、各データ型からSQLリテラルのルーチンを提供します。

    • SQLステートメントは検証が困難です
  2. nvarchar/ nchar列を使用する(および文字列リテラルの前にを付けるN)またはvarchar/ char列に入る値をASCII文字のみに制限する(SQLステートメントの作成時に例外をスローするなど)

    • このようにして、CHAR(700)からCHAR(39)への自動アポストロフィ変換(および他の同様のUnicodeハック)を回避します。
  3. 実際の列の長さに合わせて常に値の長さを検証します(長い場合は例外をスローします)

    • SQL Serverに既知の欠陥があり、トランケート時にスローされるSQLエラーをバイパスできる(サイレントトランケーションにつながる)
  4. あなたSET QUOTED_IDENTIFIERはそれが常にON

    • 注意してください、それは解析時に有効になります、すなわちコードのアクセスできないセクションでも

これら4つのポイントを順守すれば、安全であるはずです。それらのいずれかに違反した場合、SQLインジェクションの方法が開きます。


1
攻撃者が単にユニコード文字を使用しているだけでは彼のメソッドは注入を停止できないことを示しているため、この8年前の質問に対する他のすべての回答を読んでいないようです。
ホーガン

@Hogan –私はそうしましたが、私の質問には余分な価値があると思います。私が書いたものの背後には、多くの経験とテストがあります。クエリパラメータを使用する方が良いことはわかっていますが、さまざまな理由(たとえば、雇用主が古い方法を維持するように要求する)のために誰かがそれを避けなければならない状況も完全に理解しています。この場合、私の答えは非常に包括的であり、「それをしないでください」という答えよりも価値があると思います。同じ方法を示す他の回答をここに表示してください。私の削除を検討します。
miroxlav 2016

わかりました。システムが危険にさらされた場合(そうでない場合)、戻ってこの回答を削除してください...または、パラメータ化されたクエリを使用できます。
ホーガン

@Hogan –私はそれをすることに問題はありません:)しかし、私は現在、私が投稿した4つのルールを守っていれば、これを回避する既知の方法はないと主張しています。あなたが本当にそれを回避する方法があると思うなら、ただどこに指摘するだけです。
miroxlav 2016

悪いアドバイスhombre。任意の補間を無効にすることができます。
Shayne、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.