次のストアドプロシージャを作成しました。
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が存在するかどうかを確認したところ、まだそこにありました。私は何か間違ったことをしていますか?それともこれは本当に注射耐性があるのですか?私がここで尋ねようとしているのは、このようなストアドプロシージャは安全ですか?ありがとうございました。
次の理由により、このコードは正しく機能します。
SQLインジェクションが機能するためには、クエリ文字列(これは実行していません)を構築し、not単一のアポストロフィ(_'
_)をエスケープされたアポストロフィに変換する必要があります(_''
_)(これらは入力パラメーターを介してエスケープされます)。
「妥協した」値を渡そうとすると、_'Male; DROP TABLE tblActor'
_文字列は単なる文字列です。
さて、もしあなたが次の線に沿って何かをしていたら:
_DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'SELECT fields FROM table WHERE field23 = '
+ @InputParam;
EXEC(@SQL);
_
次に、thatはSQLインジェクションの影響を受けます。これは、thatクエリが現在の事前解析されたコンテキストにないためです。そのクエリは現時点では単なる別の文字列です。したがって、_@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;
_
このアプローチを使用すると、DATETIME
入力パラメーターに_'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
_を渡そうとすると、ストアドプロシージャの実行時にエラーが発生します。または、ストアドプロシージャが_@InputParameter
_をNVARCHAR(100)
として受け入れたとしても、その_sp_executesql
_呼び出しに渡すには、DATETIME
に変換する必要があります。また、ダイナミックSQLのパラメーターが文字列型であっても、最初にストアドプロシージャに入ると、単一のアポストロフィは自動的に二重のアポストロフィにエスケープされます。
あまり知られていない種類の攻撃があり、攻撃者は入力フィールドをアポストロフィで埋めようとします。これにより、動的SQLの構築に使用されるが、宣言された文字列が小さすぎるとすべてに適合できないそして、最後のアポストロフィを押し出し、どういうわけか正しい数のアポストロフィで終了して、文字列内で「エスケープ」されないようにします。これはSQL Truncationと呼ばれ、MSDNマガジンの記事「新しいSQL Truncation攻撃とそれらを回避する方法」でBala Neerumallaによって話されましたが、この記事はオンラインではありません。この記事を含む問題— MSDNマガジンの2006年11月版 —は、。chm フォーマット)。ダウンロードした場合、デフォルトのセキュリティ設定により開かない場合があります。これが発生した場合は、MSDNMagazineNovember2006en-us.chmファイルを右クリックして、[プロパティ]を選択します。これらのタブの1つに、「このタイプのファイルを信頼する」(またはそのようなもの)オプションがあり、チェック/有効化する必要があります。 [OK]ボタンをクリックして、。chmファイルをもう一度開いてみてください。
切り捨て攻撃の別のバリエーションは、ローカル変数を使用して、エスケープするために二重引用符を二重にした「安全な」ユーザー指定の値を格納することを想定し、そのローカル変数を埋めて単一引用符を配置することです。最後に。ここでの考え方は、ローカル変数のサイズが適切でない場合、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--';
_
これを修正するのは簡単です。次のいずれかを実行してください。
ローカル変数を使用して「固定」値を格納しないでください。動的SQLの作成にREPLACE()
を直接入れるだけです。
_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--';
_
上記の切り捨ての例に関するメモ:
DELETE tableName
_は破壊的ですが、バックドアユーザーを追加したり、管理者パスワードを変更したりする可能性は低くなります。SQLインジェクションに関連する詳細情報(さまざまなRDBMSとシナリオをカバー)については、 Open Web Application Security Project (OWASP)から以下を参照してください。
SQLインジェクションのテスト
SQLインジェクションとSQLトランケーションに関する関連スタックオーバーフローの回答:
'エスケープ文字を置き換えた後のT-SQLの安全性は?
簡単なことは、データをコマンドと混同しないことです。パラメータの値がコマンドの一部として扱われることはなく、したがって実行されることもありません。
私はこれについてブログに書いた: http://blogs.lobsterpot.com.au/2015/02/10/sql-injection-the-golden-rule/