ما هي مخاطر SQL الديناميكية، وهل يمكن تجنبها؟
-
06-07-2019 - |
سؤال
لقد حصلنا للتو على الكود التالي كحل لاستعلام بحث معقد في تطبيق جديد يقدمه المطورون الخارجيون.أنا متشكك في استخدام SQL الديناميكي لأنه يمكنني إغلاق عبارة SQL باستخدام ';ومن ثم تنفيذ سيئة التي سيتم تنفيذها على قاعدة البيانات!
هل لديك أي أفكار حول كيفية إصلاح هجوم الحقن؟
ALTER procedure [dbo].[SearchVenues] --'','',10,1,1,''
@selectedFeature as varchar(MAX),
@searchStr as varchar(100),
@pageCount as int,
@startIndex as int,
@searchId as int,
@venueName as varchar(100),
@range int,
@latitude varchar(100),
@longitude varchar(100),
@showAll int,
@OrderBy varchar(50),
@SearchOrder varchar(10)
AS
DECLARE @sqlRowNum as varchar(max)
DECLARE @sqlRowNumWhere as varchar(max)
DECLARE @withFunction as varchar(max)
DECLARE @withFunction1 as varchar(max)
DECLARE @endIndex as int
SET @endIndex = @startIndex + @pageCount -1
SET @sqlRowNum = ' SELECT Row_Number() OVER (ORDER BY '
IF @OrderBy = 'Distance'
SET @sqlRowNum = @sqlRowNum + 'dbo.GeocodeDistanceMiles(Latitude,Longitude,' + @latitude + ',' + @longitude + ') ' +@SearchOrder
ELSE
SET @sqlRowNum = @sqlRowNum + @OrderBy + ' '+ @SearchOrder
SET @sqlRowNum = @sqlRowNum + ' ) AS RowNumber,ID,RecordId,EliteStatus,Name,Description,
Address,TotalReviews,AverageFacilityRating,AverageServiceRating,Address1,Address2,Address3,Address4,Address5,Address6,PhoneNumber,
visitCount,referalCount,requestCount,imgUrl,Latitude,Longitude,
Convert(decimal(10,2),dbo.GeocodeDistanceMiles(Latitude,Longitude,' + @latitude + ',' + @longitude + ')) as distance
FROM VenueAllData '
SET @sqlRowNumWhere = 'where Enabled=1 and EliteStatus <> 3 '
--PRINT('@sqlRowNum ='+@sqlRowNum)
IF @searchStr <> ''
BEGIN
IF (@searchId = 1) -- county search
BEGIN
SET @sqlRowNumWhere = @sqlRowNumWhere + ' and Address5 like ''' + @searchStr + '%'''
END
ELSE IF(@searchId = 2 ) -- Town search
BEGIN
SET @sqlRowNumWhere = @sqlRowNumWhere + ' and Address4 like ''' + @searchStr + '%'''
END
ELSE IF(@searchId = 3 ) -- postcode search
BEGIN
SET @sqlRowNumWhere = @sqlRowNumWhere + ' and Address6 like ''' + @searchStr + '%'''
END
IF (@searchId = 4) -- Search By Name
BEGIN
IF @venueName <> ''
SET @sqlRowNumWhere = @sqlRowNumWhere + ' and ( Name like ''%' + @venueName + '%'' OR Address like ''%'+ @venueName+'%'' ) '
ELSE
SET @sqlRowNumWhere = @sqlRowNumWhere + ' and ( Name like ''%' + @searchStr + '%'' OR Address like ''%'+ @searchStr+'%'' ) '
END
END
IF @venueName <> '' AND @searchId <> 4
SET @sqlRowNumWhere = @sqlRowNumWhere + ' and ( Name like ''%' + @venueName + '%'' OR Address like ''%'+ @venueName+'%'' ) '
set @sqlRowNum = @sqlRowNum + ' ' + @sqlRowNumWhere
--PRINT(@sqlRowNum)
IF @selectedFeature <> ''
BEGIN
DECLARE @val1 varchar (255)
Declare @SQLAttributes varchar(max)
Set @SQLAttributes = ''
Declare @tempAttribute varchar(max)
Declare @AttrId int
while (@selectedFeature <> '')
BEGIN
SET @AttrId = CAST(SUBSTRING(@selectedFeature,1,CHARINDEX(',',@selectedFeature)-1) AS Int)
Select @tempAttribute = ColumnName from Attribute where id = @AttrId
SET @selectedFeature = SUBSTRING(@selectedFeature,len(@AttrId)+2,len(@selectedFeature))
SET @SQLAttributes = @SQLAttributes + ' ' + @tempAttribute + ' = 1 And '
END
Set @SQLAttributes = SUBSTRING(@SQLAttributes,0,LEN(@SQLAttributes)-3)
set @sqlRowNum = @sqlRowNum + ' and ID in (Select VenueId from '
set @sqlRowNum = @sqlRowNum + ' CachedVenueAttributes WHERE ' + @SQLAttributes + ') '
END
IF @showAll <> 1
set @sqlRowNum = @sqlRowNum + ' and dbo.GeocodeDistanceMiles(Latitude,Longitude,' + @latitude + ',' + @longitude + ') <= ' + convert(varchar,@range )
set @withFunction = 'WITH LogEntries AS (' + @sqlRowNum + ')
SELECT * FROM LogEntries WHERE RowNumber between '+ Convert(varchar,@startIndex) +
' and ' + Convert(varchar,@endIndex) + ' ORDER BY ' + @OrderBy + ' ' + @SearchOrder
print(@withFunction)
exec(@withFunction)
المحلول
وبوصفها جانبا، وأنا لن تستخدم EXEC
. بدلا أود أن استخدام sp_executesql
. انظر هذه المادة الرائعة، لعنة وبركاته من ديناميك SQL ، وذلك لسبب وغيرها من المعلومات على استخدام مزود الحيوية.
نصائح أخرى
وهنا لا يستخدم الصيغة الأمثل للاستعلام سبق أن SQL الديناميكي ...
Declare @selectedFeature as varchar(MAX),
@searchStr as varchar(100),
@pageCount as int,
@startIndex as int,
@searchId as int,
@venueName as varchar(100),
@range int,
@latitude varchar(100),
@longitude varchar(100),
@showAll int,
@OrderBy varchar(50),
@SearchOrder varchar(10)
Set @startIndex = 1
Set @pageCount = 50
Set @searchStr = 'e'
Set @searchId = 4
Set @OrderBy = 'Address1'
Set @showAll = 1
--Select dbo.GeocodeDistanceMiles(Latitude,Longitude,@latitude,@longitude)
DECLARE @endIndex int
SET @endIndex = @startIndex + @pageCount -1
;
WITH LogEntries as (
SELECT
Row_Number()
OVER (ORDER BY
CASE @OrderBy
WHEN 'Distance' THEN Cast(dbo.GeocodeDistanceMiles(Latitude,Longitude,@latitude,@longitude) as varchar(10))
WHEN 'Name' THEN Name
WHEN 'Address1' THEN Address1
WHEN 'RecordId' THEN Cast(RecordId as varchar(10))
WHEN 'EliteStatus' THEN Cast(EliteStatus as varchar(10))
END) AS RowNumber,
RecordId,EliteStatus,Name,Description,
Address,TotalReviews,AverageFacilityRating,AverageServiceRating,Address1,Address2,Address3,Address4,Address5,Address6,PhoneNumber,
visitCount,referalCount,requestCount,imgUrl,Latitude,Longitude,
Convert(decimal(10,2),dbo.GeocodeDistanceMiles(Latitude,Longitude,@latitude,@longitude)) as distance
FROM VenueAllData
where Enabled=1 and EliteStatus <> 3
And
(
(Address5 like @searchStr + '%' And @searchId = 1) OR
(Address4 like @searchStr + '%' And @searchId = 2) OR
(Address6 like @searchStr + '%' And @searchId = 3) OR
(
(
@searchId = 4 And
(Name like '%' + @venueName + '%' OR Address like '%'+ @searchStr+'%')
)
)
)
And
ID in (
Select VenueID
From CachedVenueAttributes
--Extra Where Clause for the processing of VenueAttributes using @selectedFeature
)
And
(
(@showAll = 1) Or
(@showAll <> 1 and dbo.GeocodeDistanceMiles(Latitude,Longitude,@latitude,@longitude) <= convert(varchar,@range ))
)
)
SELECT * FROM LogEntries
WHERE RowNumber between @startIndex and @endIndex
ORDER BY CASE @OrderBy
WHEN 'Distance' THEN Cast(Distance as varchar(10))
WHEN 'Name' THEN Name
WHEN 'Address1' THEN Address1
WHEN 'RecordId' THEN Cast(RecordId as varchar(10))
WHEN 'EliteStatus' THEN Cast(EliteStatus as varchar(10))
END
والشيء الوحيد الذي لم الثابت هو اختيار من CachedVenueAttributes يبدو أن بناء حيث بيان في حلقة. أعتقد أنني قد وضعت هذا في وظيفة الجدول الكرام، وريفاكتور في عزلة لبقية الإجراءات.
أنا أحب SQL الديناميكي للبحث.
عندما استخدمته في الماضي، استخدمت بيانات .Net المعدة مع أي سلسلة أنشأها المستخدم يتم تمريرها كمعلمة غير مضمنة كنص في SQL.
للتعامل مع الحل الحالي، يمكنك القيام بعدد من الأشياء للتخفيف من المخاطر.
- إدخال القائمة البيضاء، والتحقق من صحة الإدخال بحيث يمكن أن يحتوي فقط على a-zA-Z0-9\w (أرقام ألفا ومسافة بيضاء) (سيئ إذا كنت بحاجة إلى دعم أحرف يونيكود)
- قم بتنفيذ أي SQL ديناميكي كمستخدم مقيد.قم بتعيين مالك proc المخزن إلى مستخدم لديه حق الوصول للقراءة فقط إلى الجداول المعنية.رفض الكتابة إلى كافة الجداول إلخ.أيضًا عند استدعاء هذا proc المخزن، قد تحتاج إلى القيام بذلك مع مستخدم لديه قيود مماثلة على ما يمكنه القيام به، حيث يبدو أن MS-SQL ينفذ SQL ديناميكي داخل proc مخزن حيث أن المستخدم المتصل ليس مالك proc المخزن.
ولقد أدركت أن هذه هي وظيفة قديمة حقا، ولكن عندما تفعل أشياء مثل:
AND
(
(@showAll = 1)
OR (@showAll <> 1
AND dbo.GeocodeDistanceMiles(Latitude,Longitude,@latitude,@longitude) <= convert(varchar,@range))
)
و... وعلى OPTION(RECOMPILE)
تساعد عادة اختيار خطة أكثر إيجازا، طالما أنه لن يعدم ألف مرة في الثانية الواحدة أو أي شيء.