Was sind die Gefahren von dynamischen SQL, und können sie vermieden werden?
-
06-07-2019 - |
Frage
Wir haben gerade den folgenden Code als eine Lösung für eine komplizierte Suchabfrage in einer neuen Anwendung zur Verfügung gestellt von Offshore-Entwickler gegeben worden. Ich bin skeptisch, was die Verwendung von dynamischen SQL, weil ich die SQL-Anweisung ‚schließen konnte; und dann eine böse excute, die auf der Datenbank durchgeführt werden!
Alle Ideen, wie die Injektion Angriff zu beheben?
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)
Lösung
Als Nebenwirkung, würde ich nicht EXEC
verwenden; Ich würde eher verwenden sp_executesql
. Sehen Sie diese hervorragende Artikel, Der Fluch und Segen der dynamischen SQL , aus dem Grund, und andere Informationen über mit dynamischen sQL.
Andere Tipps
Hier ist eine optimierte Version der Abfrage oben, dass keine dynamische SQL nicht verwendet ...
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
Das einzige, was ich nicht festgelegt haben, ist die Auswahl von CachedVenueAttributes, die eine where-Anweisung in einer Schleife aufzubauen scheint. Ich glaube, ich könnte diese in einer Tabellenwertfunktion, und Refactoring es isoliert auf den Rest des Verfahrens.
Ich mag dynamische SQL für die Suche.
Wo ich es in der Vergangenheit habe ich verwendet habe .Net vorbereitete Anweisungen mit jedem Benutzer generierte Zeichenfolge als Parameter als Text nicht in der SQL enthalten übergeben werden.
mit der bestehenden Lösung ausführen können Sie eine Reihe von Dingen tun Risiko zu verringern.
- Weiße Liste Eingang, validieren Eingang, so dass es nur einen-zA-Z0-9 \ w (alpha Numerik und weißen Raum) enthält (schlecht, wenn Sie Unicode-Zeichen unterstützt werden müssen)
- Führen Sie eine beliebige dynamische SQL als eingeschränkter Benutzer. Stellen Sie Besitzer gespeicherte Prozedur für einen Benutzer, der hat nur besorgt Zugriff auf die Tabellen lesen. Schreiben verweigern zu allen Tabellen ect. Auch wenn diese gespeicherte Prozedur aufrufen müssen Sie es mit einem Benutzer mit ähnlichen Beschränkungen tun, was sie tun können, wie es appares MS-SQL innerhalb eines storedproc als anrufende Benutzer nicht der Besitzer der storedproc dynamische SQL ausführt.
Ich habe erkannt, dass dies eine wirklich alte Post, aber wenn Dinge zu tun, wie:
AND
(
(@showAll = 1)
OR (@showAll <> 1
AND dbo.GeocodeDistanceMiles(Latitude,Longitude,@latitude,@longitude) <= convert(varchar,@range))
)
... ein OPTION(RECOMPILE)
wird in der Regel helfen, einen prägnanten Plan zu wählen, solange es geht um nicht mehr als tausend Mal pro Sekunde oder irgendetwas ausgeführt werden.