次の理由により、このコードは適切に機能します。
- パラメータ化
- ない任意の動的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--';
これを修正するのは簡単です。次のいずれかを実行します。
- 絶対に必要な場合を除き、動的SQLを使用しないでください!(最初に検討する必要があるため、これを最初にリストしています)。
- ローカル変数のサイズを適切に設定します(つまり、渡されたすべての文字が単一引用符である場合に備えて、入力パラメーターの2倍のサイズにする必要があります。
「固定」値の保存にローカル変数を使用しないでください。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--';
上記の切り捨ての例に関する注意:
- はい、これは非常に不自然な例です。注入する15文字だけでできることはあまりありません。確かに、
DELETE tableName
破壊的かもしれませんが、バックドアユーザーを追加したり、管理者パスワードを変更したりする可能性は低くなります。
- この種の攻撃には、おそらくコード、テーブル名などの知識が必要です。ランダムな見知らぬ人やスクリプトキディによって行われる可能性は低いですが、私は脆弱性を知っているかなり動揺した元従業員によって攻撃された場所で働いていました他の誰も知らなかった特定のWebページで。つまり、攻撃者はシステムの詳細な知識を持っている場合があります。
- 確かに、全員のパスワードのリセットは調査される可能性が高いため、攻撃が発生していることを会社に知らせてしまう可能性がありますが、バックドアユーザーを挿入したり、後で使用/悪用するための二次情報を取得するのに十分な時間を提供する可能性があります。
- このシナリオの大部分がアカデミック(つまり、現実の世界では起こりそうにない)であっても、それは不可能ではありません。
SQLインジェクションに関連する詳細情報(さまざまなRDBMSおよびシナリオをカバー)については、Open Web Application Security Project(OWASP)の以下を参照してください
。SQLインジェクションのテスト
SQLインジェクションとSQLトランケーションに関する関連するスタックオーバーフローの答え:
'エスケープ文字を置き換えた後のT-SQLの安全性は?
EXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'