2011-01-26 2 views
4

Я использую хранимые процедуры. Чтобы сэкономить время, я сделал несколько общих процедур, которые используют динамический заказ sqlin для обновления. Такая типовая процедура:Как предотвратить SQL Injection

CREATE PROCEDURE [dbo].[SetField] 
@company_id uniqueidentifier, 
@id bigint, 
@field_code nvarchar(50), 
@value nvarchar(50) 
AS 
BEGIN 
DECLARE @field_name nvarchar(50) 
SET @field_name = NULL 
SELECT @field_name=field_name 
FROM dbo.FIELD_DEFINITION 
WHERE [email protected]_code 

IF @field_name IS NOT NULL 
BEGIN 

    IF @value IS NULL OR @value='' 
    BEGIN 
    SET @value='NULL' 
    END 
    ELSE 
    BEGIN 
    IF @field_code='START_DATE' OR @field_code='END_DATE' 
    BEGIN 
    SET @value = CONVERT(datetime, @value ,103) 
    END 
    SET @value=''''[email protected]+'''' 
    END 

    DECLARE @sql nvarchar(1000) 
    SET @sql = 'UPDATE dbo.TABLE '+ 
    'SET '[email protected]_name+'='[email protected]+' '+ 
    'WHERE company_id=''' + CAST(@company_id as nvarchar(36)) + ''' AND '+ 
    'id='+CAST(@id as nvarchar) 
    EXEC(@sql) 
END 
END 

Как я могу предотвратить инъекцию sql с помощью этого кода?

+2

Вы имеете в виду, кроме очевидного ответа, не используя динамический SQL в хранимой процедуре? – Thomas

+0

@Thomas: Можете ли вы показать мне, как сделать следующее без динамического sql ?? – Naor

+0

Попытка обобщить слишком много в программировании базы данных - это плохо. Напишите настоящие procs, которые делают то, что вам нужно сделать, не пытаясь сделать этот динамический материал, который вызовет больше проблем, чем он решает, поскольку он почти невозможно адекватно тестировать, а также рисковать инъекцией. – HLGEM

ответ

4

Важным аспектом, который следует помнить о внедрении SQL, является то, что, если это вообще возможно, , вы никогда не должны вставлять введенные пользователем значения непосредственно в ваш SQL-код. Это не означает, что вы не можете использовать динамический sql (хотя это определенно облегчает вам работу, если вы не), но иногда это становится более опасным.

В вашем конкретном примере вы можете сохранить параметризацию всего, кроме @field_name. Это, к сожалению, должно быть встроено непосредственно в SQL; все остальное может быть передано как параметр снова, поэтому нет необходимости беспокоиться об их содержании.

Самое безопасное, что вы можете сделать в этом конкретном примере, состоит в следующем:

if(exists (select 1 from INFORMATION_SCHEMA.Columns where TABLE_NAME = 'Table' and TABLE_SCHEMA = 'dbo' and COLUMN_NAME = @fieldName)) 
begin 
    DECLARE @sql nvarchar(1000) 
    SET @sql = 'UPDATE dbo.TABLE '+ 
     'SET ' + QUOTENAME(@field_name) + '[email protected] ' + 
     'WHERE [email protected]_id AND '+ 
     '[email protected]' 

    exec sp_executesql @sql,N'@id bigint, @company_id uniqueidentifier, @value nvarchar(50)',@id,@company_id,@value 
end 

Это делает две вещи:

  1. Он проверяет, что есть на самом деле столбец с таким именем в Таблица. Если бы пользователь вставлял любые другие операторы SQL в это поле, то эта проверка завершилась бы неудачно, и оператор не будет выполнен. Вы также можете позвонить по телефону raiseerror, чтобы сообщить об ошибке, но я оставлю это упражнение вам.
  2. Он содержит имя поля в квадратных скобках, так что имена полей, содержащие пробелы или зарезервированные слова, не будут нарушать оператор. Это может не быть проблемой для вас, но это всегда хорошая практика, если вы сами создаете SQL.
+0

Использовать 'quotename' не связывать квадратные скобки самостоятельно! Ваш код не будет обрабатывать имена полей, содержащие символ ']' правильно. –

+0

@Martin: Спасибо за подсказку. –

+0

@Martin: Что, если @value может быть строкой или bigint? в моей версии он всегда работал. В вашей версии он говорит мне: «Ошибка преобразования типа данных nvarchar в bigint». потому что поле (в @field_name) является bigint. Как его автоматически преобразовать? – Naor

0

Я бы начал искать способы предотвращения атак с SQL-вторжением, прежде чем вы когда-нибудь позвоните в SP. Будьте осторожны с динамическими строками SQL, собранными вместе с запросами или данными формы. Используйте объект SqlCommand.

Редактировать: В ответ на комментарий здесь приведено хорошее объяснение того, как параметризованные запросы (запросы SqlCommand) помогают предотвратить SQL-инъекцию.

От http://forums.asp.net/t/1568268.aspx:

... Заполнитель - @Id - стала частью закодированного SQL. Во время выполнения значение, предоставленное запросом, передается в базу данных вместе с жестко запрограммированным SQL, и база данных проверяет поле ProductID, когда пытается связать значение параметра с ним. Это обеспечивает уровень сильной типизации. Если значение параметра не является правильным типом для поля базы данных (строка или числовые данные вне диапазона для типа поля), база данных не сможет преобразовать ее в нужный тип и отклонит ее. Если тип данных целевого поля является строкой (char, nvarchar и т. Д.), Значение параметра будет «стрифицировано» автоматически, что включает в себя экранирование одиночных кавычек. Он не станет частью выполняемого SQL-оператора.

+0

Я использую SqlCommand для вызова хранимой процедуры. Как это может помочь мне предотвратить инъекцию sql? – Naor

+0

Как я вижу, сам запрос должен быть в БД, а не в коде. – Naor

+0

Ну, я думаю, что SqLCommand работает, чтобы помочь предотвратить SQL-инъекцию, параметры хранимой процедуры строго типизированы и не могут содержать сам запрос. Не так с динамическим SQL. – SquidScareMe

6

Вы сказали:

Для того, чтобы сэкономить время, я сделал некоторые общие процедуры, которые использует динамический SQL, чтобы обновить

Если бы вы спросили первых, мы могли бы иметь сэкономленное время и предложили это ...

UPDATE 
    dbo.TABLE 
SET 
    Field1 = CASE WHEN @field_name = 'Field1' THEN @value ELSE Field1 END, 
    Field2 = CASE WHEN @field_name = 'Field2' THEN @value ELSE Field2 END, 
    Field3 = CASE WHEN @field_name = 'Field3' THEN @value ELSE Field3 END, 
    ... 
    Fieldn = CASE WHEN @field_name = 'Fieldn' THEN @value ELSE Fieldn END 
WHERE 
    company_id = @company_id AND id = @id 
+0

+1 - Ненавижу это, когда я пропускаю очевидный ответ. – Thomas

+0

@gbn: Но с помощью этого метода каждый вызов этого sp должен изменять только одно поле. Эта процедура обновляет всю строку. Является ли это эффективным? – Naor

+0

Не могу вспомнить ответ на этот вопрос. Довольно точно его охват [Влияние не обновляющихся обновлений] (http://sqlblog.com/blogs/paul_white/archive/2010/08/11/the_2D00_impact_2D00_of_2D00_update_2D00_statements_2D00_that_2D00_don_2D00_t_2D00_change_2D00_data.aspx) –

Смежные вопросы