ストアドプロシージャ内のこのクエリでSQLインジェクションが発生しないのはなぜですか?


18

次のストアドプロシージャを作成しました。

ALTER PROCEDURE usp_actorBirthdays (@nameString nvarchar(100), @actorgender nvarchar(100))
AS
SELECT ActorDOB, ActorName FROM tblActor
WHERE ActorName LIKE '%' + @nameString + '%'
AND ActorGender = @actorgender

今、私はこのようなことをしてみました。私はこれを間違っているかもしれませんが、そのような手順がSQLインジェクションを防ぐことができることを確認したいです:

EXEC usp_actorBirthdays 'Tom', 'Male; DROP TABLE tblActor'

以下の画像は、上記のSQLがSSMSで実行され、エラーではなく結果が正しく表示されることを示しています。

ここに画像の説明を入力してください

ところで、クエリの実行が完了した後、セミコロンの後にその部分を追加しました。その後、再度実行しましたが、テーブルtblActorが存在するかどうかを確認したところ、まだそこにありました。私は何か間違っていますか?または、これは本当に注射防止ですか?私がここで尋ねようとしているのは、このような安全なストアドプロシージャですか?ありがとうございました。


これを試しましたかEXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'
-MarmiK

回答:


38

次の理由により、このコードは適切に機能します。

  1. パラメータ化
  2. ない任意の動的SQLを実行します

仕事へのSQLインジェクションためには、あなたは(あなたがやっていない)クエリ文字列を構築し、する必要はありません(単一アポストロフィを翻訳'()エスケープ-アポストロフィに'')(これらは、入力パラメータを経由してエスケープされます)。

「妥協した」値を渡そうとすると、'Male; DROP TABLE tblActor'文字列は単なるプレーン文字列になります。

さて、あなたが以下のラインに沿って何かをしていたら

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'SELECT fields FROM table WHERE field23 = '
          + @InputParam;

EXEC(@SQL);

そして、それがため、SQLインジェクションの影響を受けやすくなり、そのクエリは現在、事前解析されたコンテキストではありません。そのクエリは現時点では単なる文字列です。値はそう@InputParamかもしれない'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;し、そのクエリは次のように、レンダリング、および実行されますので、それが問題を提示可能性があります。

SELECT fields FROM table WHERE field23 = '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;

これは、ストアドプロシージャを使用する主な理由の1つです:本質的に安全です(使用したパラメーターの値を検証せずに上記で示したようなクエリを作成して、そのセキュリティを回避しない限り)。ただし、動的SQLを構築する必要がある場合は、以下を使用してパラメータ化することをお勧めしますsp_executesql

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'SELECT fields FROM table WHERE field23 = @SomeDate_tmp';

EXEC sp_executesql
  @SQL,
  N'SomeDate_tmp DATETIME',
  @SomeDate_tmp = @InputParam;

このアプローチを使用する'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;と、DATETIME入力パラメータを渡そうとすると、ストアドプロシージャの実行時にエラーが発生します。または、ストアドプロシージャが@InputParameterとして受け入れられた場合でも、その呼び出しに渡すためにNVARCHAR(100)に変換する必要DATETIMEがありsp_executesqlます。また、動的SQLのパラメーターが文字列型である場合でも、最初にストアドプロシージャに入ると、単一のアポストロフィが自動的に二重アポストロフィにエスケープされます。

あまり知られていないタイプの攻撃があり、攻撃者が入力フィールドをアポストロフィで埋めようとするため、動的SQLの構築に使用されるストアドプロシージャ内の文字列が小さすぎると宣言されてすべてに適合しない場合があります。そして、末尾のアポストロフィを押し出し、文字列内で「エスケープ」されないように、どういうわけか正しい数のアポストロフィで終了します。これはSQL Truncationと呼ばれ、MSNマガジンのBala Neerumallaによる「新しいSQL Truncation攻撃とその回避方法」という記事で取り上げられましたが、この記事はオンラインではありません。この記事を含む問題-MSDN Magazine 2006年11月版は、Windowsヘルプファイル(.chmフォーマット)。ダウンロードした場合、デフォルトのセキュリティ設定により開かない場合があります。この場合、MSDNMagazineNovember2006en-us.chmファイルを右クリックして、[プロパティ]を選択します。それらのタブの1つには、チェック/有効化する必要がある「このタイプのファイルを信頼する」オプション(またはそのようなもの)があります。[OK]ボタンをクリックしてから、.chmファイルをもう一度開いてみます。

Truncation攻撃の別のバリエーションは、ローカル変数を使用して「安全な」ユーザー指定の値を格納するために使用され、エスケープするためにシングルクォートが二重化され、そのローカル変数を埋めてシングルクォートを配置することです最後に。ここでの考え方は、ローカル変数のサイズが適切でない場合、最後に2番目のシングルクォートのための十分なスペースがなく、変数をシングルクォートで終了し、それがシングルクォートと結合することです。動的SQLのリテラル値を終了し、その末尾の単一引用符を埋め込みのエスケープされた単一引用符に変え、動的SQLの文字列リテラルは、次の文字列リテラルを開始するための次の単一引用符で終了します。例えば:

-- Parameters:
DECLARE @UserID      INT = 37,
        @NewPassword NVARCHAR(15) = N'Any Value ....''',
        @OldPassword NVARCHAR(15) = N';Injected SQL--';

-- Stored Proc:
DECLARE @SQL NVARCHAR(MAX),
        @NewPassword_fixed NVARCHAR(15) = REPLACE(@NewPassword, N'''', N''''''),
        @OldPassword_fixed NVARCHAR(15) = REPLACE(@OldPassword, N'''', N'''''');

SELECT @NewPassword AS [@NewPassword],
       REPLACE(@NewPassword, N'''', N'''''') AS [REPLACE output],
       @NewPassword_fixed AS [@NewPassword_fixed];
/*
@NewPassword          REPLACE output          @NewPassword_fixed
Any Value ....'       Any Value ....''        Any Value ....'
*/

SELECT @OldPassword AS [@OldPassword],
       REPLACE(@OldPassword, N'''', N'''''') AS [REPLACE output],
       @OldPassword_fixed AS [@OldPassword_fixed];
/*
@OldPassword          REPLACE output          @OldPassword_fixed
;Injected SQL--       ;Injected SQL--         ;Injected SQL--
*/

SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
           + @NewPassword_fixed + N''' WHERE [TableNameID] = '
           + CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
           + @OldPassword_fixed + N''';';

SELECT @SQL AS [Injected];

ここで、実行される動的SQLは次のとおりです。

UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';

より読みやすい形式の同じ動的SQLは次のとおりです。

UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';

Injected SQL--';

これを修正するのは簡単です。次のいずれかを実行します。

  1. 絶対に必要な場合を除き、動的SQLを使用しないでください!(最初に検討する必要があるため、これを最初にリストしています)。
  2. ローカル変数のサイズを適切に設定します(つまり、渡されたすべての文字が単一引用符である場合に備えて、入力パラメーターの2倍のサイズにする必要があります。
  3. 「固定」値の保存にローカル変数を使用しないでください。REPLACE()動的SQLの作成に直接配置するだけです。

    SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
               + REPLACE(@NewPassword, N'''', N'''''') + N''' WHERE [TableNameID] = '
               + CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
               + REPLACE(@OldPassword, N'''', N'''''') + N''';';
    
    SELECT @SQL AS [No SQL Injection here];

    ダイナミックSQLが侵害されることはなくなりました。

    UPDATE dbo.TableName SET [Password] = N'Any Value ....''' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';

上記の切り捨ての例に関する注意:

  1. はい、これは非常に不自然な例です。注入する15文字だけでできることはあまりありません。確かに、DELETE tableName破壊的かもしれませんが、バックドアユーザーを追加したり、管理者パスワードを変更したりする可能性は低くなります。
  2. この種の攻撃には、おそらくコード、テーブル名などの知識が必要です。ランダムな見知らぬ人やスクリプトキディによって行われる可能性は低いですが、私は脆弱性を知っているかなり動揺した元従業員によって攻撃された場所で働いていました他の誰も知らなかった特定のWebページで。つまり、攻撃者はシステムの詳細な知識を持っている場合があります。
  3. 確かに、全員のパスワードのリセットは調査される可能性が高いため、攻撃が発生していることを会社に知らせてしまう可能性がありますが、バックドアユーザーを挿入したり、後で使用/悪用するための二次情報を取得するのに十分な時間を提供する可能性があります。
  4. このシナリオの大部分がアカデミック(つまり、現実の世界では起こりそうにない)であっても、それは不可能ではありません。

SQLインジェクションに関連する詳細情報(さまざまなRDBMSおよびシナリオをカバー)については、Open Web Application Security Project(OWASP)の以下を参照してください
。SQLインジェクションのテスト

SQLインジェクションとSQLトランケーションに関する関連するスタックオーバーフローの答え:
'エスケープ文字を置き換えた後のT-SQLの安全性は?


2
ああ、どうもありがとう、これは素晴らしい答えです。今、私は分かる。最後に、攻撃者が入力フィールドをアポストロフィで埋めようとする場合、最後にあなたが言及したテクニックも見たいです。前もって感謝します。見つけられない場合に備えて、これを開いたままにしておきます。これを答えとして選択します。
ラビ

1
@Raviリンクを見つけましたが、それらはすべてアーカイブされているため、記事には到達しません。しかし、いくつかの情報と便利なリンクを追加し、それらのアーカイブ内で記事を探しています。
ソロモンラッツキー

1
srutzskyに感謝します。OWASPの記事と注入テストを読みます。正しく覚えていれば、セキュリティテストの脆弱なWebアプリである「mutillidae」には、大学で「OR 1 = 1」文字列を使用して実行したSQLインジェクションがあり、mutillidaeでは管理者としてWebアプリにログインしましたと思います。そのとき、私は最初にSQLインジェクションを紹介されました。
ラビ

1
.chmファイルも表示できませんが、この完璧な答えと、stackoverflowからのリンクやOWASPからのリンクを含むすべての有用なリンクに感謝します。今日読んで、これから多くを学びました。
ラビ

2

簡単なことは、データをコマンドと混同しないことです。パラメーターの値がコマンドの一部として扱われることはないため、実行されることはありません。

これについては、http//blogs.lobsterpot.com.au/2015/02/10/sql-injection-the-golden-rule/にブログを掲載しました。


ロブ、ありがとう。私はこの本をブックマークして、後で読むために持っています。
ラヴィ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.