これが私がこれまでに持っているものです。columnNames (後で説明します) を除いて、sp_executesql を使用してパラメーター化されていない各列をテストしました。
最終的に columnNames をテストした方法に完全に満足しているわけではありませんが、これが本当に問題だとは思いません。まず最初に、columnList がユーザーではなくアプリケーションから入ってくることを期待します。ユーザーは、最初の 7 つのパラメーターを入力するためにデータベースの構造について十分に知っているべきではなく、開発者は SQL インジェクションよりもはるかに多くのリスクを抱えています。そうは言っても、columnListが配置されている場所を考えると機能する唯一のSQLインジェクションは、両側にスペースを含む「FROM」という単語を含める必要があります。そのため、それをテストし、発生した場合はエラーを発生させました。「この列は他の場所からのものです」という名前の列がある場合、これは問題になりますが、そうでないことを願っています。
ただのメモ。sys.all_objects
システム テーブルがプロシージャによってクエリされる場合に備えて、テストにはおよびsys.all_columns
システム ビューを使用しました。それらをより一般的に使用されるものに変更するsys.objects
とsys.columns
、ストアド プロシージャはシステム ビューに対して機能しなくなります。
編集: columnList にテストを追加して、セミコロンをチェックしました。の線に沿った何か'''abc''; print ''test''; select * '
が問題になることに気づきました。
EDIT2: 列リストをより適切に処理する手順を更新しました。ありがとう@HABO!。また、値の前に [] 、 " 、 、および列リストの * の一般的な形式が含まれている場合に処理するコードを追加しましQUOTENAME
た。 @HABO がリストしたスプリッター関数を使用しているので、ここには載せません。
ALTER PROCEDURE [dbo].[SafeSqlSP_SelectGivenTableWithOptionalColFilter]
@columnList nvarchar(max) ='*',
@tableSchema sysname ,
@tableName sysname ,
@ColNameAsFilter1 nvarchar(255) ='',
@ColNameAsFilter2 nvarchar(255) ='',
@ColFilter1VAL nvarchar(max)='',
@ColFilter2VAL nvarchar(max)=''
AS
BEGIN
SET NOCOUNT ON;
--====================================================
-- Set default values
IF ISNULL(@tableSchema,'') = ''
SET @tableSchema = 'dbo'
ELSE
SET @tableSchema = LTRIM(RTRIM(@tableSchema))
IF ISNULL(@columnList,'') = ''
SET @columnList = '*'
SET @tableName = ISNULL(LTRIM(RTRIM(@tableName)),'')
SET @ColNameAsFilter1 = ISNULL(LTRIM(RTRIM(@ColNameAsFilter1)),'')
SET @ColNameAsFilter2 = ISNULL(LTRIM(RTRIM(@ColNameAsFilter2)),'')
SET @ColFilter1VAL = ISNULL(@ColFilter1VAL,'')
SET @ColFilter2VAL = ISNULL(@ColFilter2VAL,'')
--====================================================
-- Remove probably QUOTENAMEs from @tableSchema and @tableName before testing them
SET @tableSchema = CASE WHEN LEFT(@tableSchema,1) = '[' AND RIGHT(@tableSchema,1) = ']'
THEN SUBSTRING(REPLACE(@tableSchema,']]',']'),2,LEN(REPLACE(@tableSchema,']]',']'))-2)
WHEN LEFT(@tableSchema,1) = '"' AND RIGHT(@tableSchema,1) = '"'
THEN SUBSTRING(REPLACE(@tableSchema,'""','"'),2,LEN(REPLACE(@tableSchema,'""','"'))-2)
WHEN LEFT(@tableSchema,1) = '''' AND RIGHT(@tableSchema,1) = ''''
THEN SUBSTRING(REPLACE(@tableSchema,'''''',''''),2,LEN(REPLACE(@tableSchema,'''''',''''))-2)
ELSE @tableSchema END
SET @tableName = CASE WHEN LEFT(@tableName,1) = '[' AND RIGHT(@tableName,1) = ']'
THEN SUBSTRING(REPLACE(@tableName,']]',']'),2,LEN(REPLACE(@tableName,']]',']'))-2)
WHEN LEFT(@tableName,1) = '"' AND RIGHT(@tableName,1) = '"'
THEN SUBSTRING(REPLACE(@tableName,'""','"'),2,LEN(REPLACE(@tableName,'""','"'))-2)
WHEN LEFT(@tableName,1) = '''' AND RIGHT(@tableName,1) = ''''
THEN SUBSTRING(REPLACE(@tableName,'''''',''''),2,LEN(REPLACE(@tableName,'''''',''''))-2)
ELSE @tableName END
--====================================================
-- Test to make sure the schema.table exists
IF NOT EXISTS (
SELECT 1
FROM sys.all_objects
JOIN sys.schemas
ON sys.all_objects.schema_id = sys.schemas.schema_id
WHERE sys.all_objects.name = @tableName
AND sys.schemas.name = @tableSchema
AND sys.all_objects.[TYPE] IN ('S','U','V')
)
BEGIN
RAISERROR (N'Table %s.%s does not exist.',
16,
1,
@tableSchema,
@tableName)
RETURN
END
--====================================================
-- Test to make sure all of the comma delimited values
-- are valid columns for schema.table
-- Create and populate a list of columns from columnlist
DECLARE @ColumnListTable TABLE (Item varchar(255))
INSERT INTO @ColumnListTable
SELECT Item
FROM dbo.SplitCSL(@columnList)
-- Remove any extra spaces
UPDATE @ColumnListTable SET Item = LTRIM(RTRIM(Item))
-- "Fix" any * formats to a single format of [schema].[tablename].*
UPDATE @ColumnListTable SET Item = CASE WHEN Item IN (
@tableName + '.*', @tableName + '.[*]',
'[' + @tableName + '].*', '[' + @tableName + '].[*]',
@tableSchema + '.' + @tableName + '.*', @tableSchema + '.' + @tableName + '.[*]',
@tableSchema + '.' + '[' + @tableName + '].*', @tableSchema + '.' + '[' + @tableName + '].[*]',
'[' + @tableSchema + '].' + @tableName + '.*', '[' + @tableSchema + '].' + @tableName + '.[*]',
'[' + @tableSchema + '].' + '[' + @tableName + '].*', '[' + @tableSchema + '].' + '[' + @tableName + '].[*]'
)
THEN '[' + @tableSchema + '].' + '[' + @tableName + '].*'
WHEN Item IN ('*','[*]') THEN '*'
ELSE Item END
--====================================================
-- Remove probably QUOTENAMEs from columns in column list before testing them
UPDATE @ColumnListTable SET Item =
CASE WHEN LEFT(Item,1) = '[' AND RIGHT(Item,1) = ']'
THEN SUBSTRING(REPLACE(Item,']]',']'),2,LEN(REPLACE(Item,']]',']'))-2)
WHEN LEFT(Item,1) = '"' AND RIGHT(Item,1) = '"'
THEN SUBSTRING(REPLACE(Item,'""','"'),2,LEN(REPLACE(Item,'""','"'))-2)
WHEN LEFT(Item,1) = '''' AND RIGHT(Item,1) = ''''
THEN SUBSTRING(REPLACE(Item,'''''',''''),2,LEN(REPLACE(Item,'''''',''''))-2)
ELSE Item END
-- Check for invalid column names
DECLARE @ColumnListFailures AS varchar(max)
SET @ColumnListFailures = ''
SELECT @ColumnListFailures = STUFF((
SELECT ', ' + Item
FROM @ColumnListTable
WHERE Item NOT IN (SELECT name
FROM sys.all_columns
WHERE object_id = OBJECT_ID(@tableSchema+'.'+@tableName))
AND Item <> '[' + @tableSchema + '].' + '[' + @tableName + '].*'
FOR XML PATH(''),TYPE).value('.','VARCHAR(MAX)')
,1,2, '')
IF LEN(@ColumnListFailures) > 0
BEGIN
RAISERROR (N'Table %s.%s does not have columns %s that are listed in the columnList parameter.',
16,
1,
@tableSchema,
@tableName,
@ColumnListFailures)
RETURN
END
-- QUOTENAME each of the column names and re-create @ColumnList
SELECT @ColumnList = STUFF((
SELECT ', ' + CASE WHEN Item = '[' + @tableSchema + '].' + '[' + @tableName + '].*' THEN Item
ELSE QUOTENAME(Item) END
FROM @ColumnListTable
FOR XML PATH(''),TYPE).value('.','VARCHAR(MAX)')
,1,2, '')
--====================================================
-- Remove probably QUOTENAMEs from first and second column filters before testing them
SET @ColNameAsFilter1 = CASE WHEN LEFT(@ColNameAsFilter1,1) = '[' AND RIGHT(@ColNameAsFilter1,1) = ']'
THEN SUBSTRING(REPLACE(@ColNameAsFilter1,']]',']'),2,LEN(REPLACE(@ColNameAsFilter1,']]',']'))-2)
WHEN LEFT(@ColNameAsFilter1,1) = '"' AND RIGHT(@ColNameAsFilter1,1) = '"'
THEN SUBSTRING(REPLACE(@ColNameAsFilter1,'""','"'),2,LEN(REPLACE(@ColNameAsFilter1,'""','"'))-2)
WHEN LEFT(@ColNameAsFilter1,1) = '''' AND RIGHT(@ColNameAsFilter1,1) = ''''
THEN SUBSTRING(REPLACE(@ColNameAsFilter1,'''''',''''),2,LEN(REPLACE(@ColNameAsFilter1,'''''',''''))-2)
ELSE @ColNameAsFilter1 END
SET @ColNameAsFilter2 = CASE WHEN LEFT(@ColNameAsFilter2,1) = '[' AND RIGHT(@ColNameAsFilter2,1) = ']'
THEN SUBSTRING(REPLACE(@ColNameAsFilter2,']]',']'),2,LEN(REPLACE(@ColNameAsFilter2,']]',']'))-2)
WHEN LEFT(@ColNameAsFilter2,1) = '"' AND RIGHT(@ColNameAsFilter2,1) = '"'
THEN SUBSTRING(REPLACE(@ColNameAsFilter2,'""','"'),2,LEN(REPLACE(@ColNameAsFilter2,'""','"'))-2)
WHEN LEFT(@ColNameAsFilter2,1) = '''' AND RIGHT(@ColNameAsFilter2,1) = ''''
THEN SUBSTRING(REPLACE(@ColNameAsFilter2,'''''',''''),2,LEN(REPLACE(@ColNameAsFilter2,'''''',''''))-2)
ELSE @ColNameAsFilter2 END
--====================================================
-- Check that the first filter column name is valid
IF @ColNameAsFilter1 <> '' AND
NOT EXISTS (SELECT 1
FROM sys.all_columns
WHERE object_id = OBJECT_ID(@tableSchema+'.'+@tableName)
AND name = @ColNameAsFilter1)
BEGIN
RAISERROR (N'Table %s.%s does not have a column %s.',
16,
1,
@tableSchema,
@tableName,
@ColNameAsFilter1)
RETURN
END
--====================================================
-- Check that the second filter column name is valid
IF @ColNameAsFilter2 <> '' AND
NOT EXISTS (SELECT 1
FROM sys.all_columns
WHERE object_id = OBJECT_ID(@tableSchema+'.'+@tableName)
AND name = @ColNameAsFilter2)
BEGIN
RAISERROR (N'Table %s.%s does not have a column %s.',
16,
1,
@tableSchema,
@tableName,
@ColNameAsFilter2)
RETURN
END
--====================================================
-- Construct & execute the dynamic SQL
DECLARE @sqlCommand nvarchar(max)
SET @sqlCommand = 'SELECT ' + @columnList + CHAR(13) +
' FROM [' + @tableSchema + '].['+ @tableName + ']' + CHAR(13) +
' WHERE 1=1 '
IF @ColNameAsFilter1 != ''
SET @sqlCommand = @sqlCommand + CHAR(13) +
' AND ' + QUOTENAME(@ColNameAsFilter1) + ' = @ColFilter1VAL'
IF @ColNameAsFilter2 != ''
SET @sqlCommand = @sqlCommand + CHAR(13) +
' AND ' + QUOTENAME(@ColNameAsFilter2) + ' = @ColFilter2VAL'
EXECUTE sp_executesql @sqlCommand,
N'@ColFilter1VAL nvarchar(75), @ColFilter2VAL nvarchar(75)',
@ColFilter1VAL, @ColFilter2VAL
END