Как очистить динамический SQL в SQL Server - предотвратить внедрение SQL - PullRequest
9 голосов
/ 05 ноября 2010

У нас есть тонна хранимых процедур SQL Server, которые используют динамический SQL.

Параметры хранимой процедуры используются в инструкции динамического SQL.

Нам нужна стандартная функция проверкивнутри этих хранимых процедур для проверки этих параметров и предотвращения внедрения SQL.

Предположим, у нас есть следующие ограничения:

  1. Мы не можем переписать процедуры, чтобы они не использовали Dynamic SQL

  2. Мы не можем использовать sp_OACreate и т. Д., Чтобы использовать регулярные выражения для проверки.

  3. Мы не можем изменить приложение, которое вызывает сохраненныйпроцедура проверки параметров перед их передачей в хранимую процедуру.

Существует ли набор символов, которые мы можем отфильтровать, чтобы гарантировать, что мы не подвержены внедрению SQL?

Ответы [ 8 ]

10 голосов
/ 05 ноября 2010

Я считаю, что есть три разных случая, о которых вам нужно беспокоиться:

  • строки (все, что требует кавычек): '''' + replace(@string, '''', '''''') + ''''
  • имена (все, что не в кавычках)t позволено): quotename(@string)
  • вещей, которые нельзя заключать в кавычки: для этого требуется белый список

Примечание : Все в строковой переменной(char, varchar, nchar, nvarchar и т. Д.), Поступающий из управляемых пользователем источников, должен использовать один из указанных выше методов.Это означает, что даже те вещи, которые вы ожидаете, чтобы быть числами, заключаются в кавычки, если они хранятся в строковых переменных.

Подробнее см. Microsoft Magazine (Устаревшая ссылка: 2016-10-19) .

Вот пример использования всех трех методов:

EXEC 'SELECT * FROM Employee WHERE Salary > ''' +
     REPLACE(@salary, '''', '''''') +   -- replacing quotes even for numeric data
     ''' ORDER BY ' + QUOTENAME(@sort_col) + ' ' +  -- quoting a name
     CASE @sort_dir WHEN 'DESC' THEN 'DESC' END     -- whitelisting

Также обратите внимание, что при выполнении всех строковых операций, встроенных в EXEC заявление, что нет проблем с усечением.Если вы присваиваете промежуточные результаты переменным, вы должны убедиться, что переменные достаточно велики для хранения результатов.Если вы введете SET @result = QUOTENAME(@name), вы должны определить @result, чтобы он содержал не менее 258 (2 * 128 + 2) символов.Если вы сделаете SET @result = REPLACE(@str, '''', ''''''), вы должны определить, что @result будет в два раза больше @str (предположим, что каждый символ в @str может быть кавычкой).И, конечно же, строковая переменная, содержащая окончательный оператор SQL, должна быть достаточно большой, чтобы в ней содержался весь статический SQL плюс все переменные результата.

6 голосов
/ 05 ноября 2010

Тривиальные случаи можно исправить с помощью QUOTENAME и ЗАМЕНИТЬ:

set @sql = N'SELECT ' + QUOTENAME(@column) + 
   N' FROM Table WHERE Name = ' + REPLACE(@name, '''', '''''');

Хотя QUOTENAME можно использовать и для литералов, чтобы добавлять одинарные кавычки и заменять одинарные кавычки двойными одинарными кавычками, поскольку он усекает ввод до 128 символов, но это не рекомендуется.

Но это только верхушка айсберга. Есть несколько составных имен (dbo.table), о которых вам нужно позаботиться: цитирование составного имени приведет к неверному идентификатору [dbo.table], его необходимо проанализировать и разделить (используя PARSENAME), затем правильно цитируется в [dbo].[table].

Другая проблема - это атаки с усечением, которые могут произойти, даже если вы выполняете тривиальную ЗАМЕНУ для литералов, см. Новые атаки с усечением SQL и как их избежать .

Проблема SQL-инъекции никогда не может быть решена с помощью одной магической функции, помещенной в каждую процедуру. Это все равно что спросить: «Мне нужна функция, которая заставит мой код работать быстрее». Предотвращение инъекционных атак - это и сквозная игра, требующая дисциплины кодирования вплоть до , ее нельзя просто добавить в качестве запоздалой мысли. Ваш лучший шанс - проверить каждую отдельную процедуру и проанализировать код T-SQL построчно , открывая глаза на уязвимости, а затем устранять проблемы по мере их обнаружения.

4 голосов
/ 05 ноября 2010

С этими ограничениями вы просто облажались.

Вот два варианта, которые могут дать вам некоторое направление:

  1. Используйте валидатор / анализатор белого списка, который принимает запросы только в формате и с ожидаемыми ключевыми словами и таблицами. Это, вероятно, будет работать только с очень хорошим парсером SQL, который действительно понимает синтаксис.

  2. Выполнение запросов в ограниченной среде. Например, используйте учетную запись пользователя с очень ограниченными правами. Например, разрешить (только чтение) доступ к определенным представлениям, которые никогда не вернут конфиденциальные данные, и запретить доступ ко всем другим представлениям, всем хранимым процедурам, функциям и таблицам. Еще безопаснее выполнить эти запросы на другом сервере базы данных. Также не забудьте отключить команду OPENROWSET .

Обратите внимание на следующее:

  1. Когда вы принимаете все запросы, кроме тех, которые имеют недопустимые ключевые слова, вы обязательно потерпите неудачу, потому что черный список всегда терпит неудачу. Особенно с таким сложным языком, как SQL.

  2. Не забывайте, что использование динамического SQL из источников, которым вы не можете доверять, является злом в самом чистом смысле, даже если вы используете эти советы, потому что время от времени обнаруживаются ошибок , которые злоупотреблять, отправляя специально созданный SQL на сервер. Поэтому, даже если вы примените эти советы, риск все равно остается.

  3. Когда вы решите пойти с решением, которое позволяет динамический SQL. Пожалуйста, не думайте, что вы можете найти себе безопасное решение, особенно если вы пытаетесь защитить конфиденциальные бизнес-данные. Наймите специалиста по безопасности сервера баз данных, чтобы помочь вам в этом.

3 голосов
/ 05 ноября 2010

Это действительно неприятная проблема, ее не проблема, которую вы хотите решить, однако вот тривиальное дело, которое работает (рецензенты, пожалуйста, дайте мне знать, если я пропустил дело, это приходитс NO гарантиями

create proc Bad 
  @param nvarchar(500) 
as 

exec (N'select ''' + @param + N'''') 

go

-- oops injected
exec Bad 'help'' select ''0wned!'' select ''' 

go 

create proc NotAsBad
   @param nvarchar(500) 
as 

declare @safish nvarchar(1000), @sql nvarchar(2000) 
set @safish = replace(@param, '''', '''''')

set @sql = N'select ''' + @safish  + N''''

exec (@sql) 

go 

-- this kind of works, but I have not tested everything
exec NotAsBad 'help'' select ''0wned!'' select ''' 
2 голосов
/ 25 октября 2017

Существует ли набор символов, которые мы можем отфильтровать, чтобы гарантировать, что мы не подвержены внедрению SQL?

NO

Внедрение SQL не называется «Внедрение определенного набора символов», и по причине. Фильтрация определенного символа может усложнить конкретный эксплойт, но не предотвращает саму инъекцию SQL. Чтобы использовать инъекцию SQL, нужно написать SQL. И SQL не ограничивается несколькими специальными символами.

2 голосов
/ 05 ноября 2010

OWASP обладает некоторой информацией об этой стратегии.Это всегда должен быть последний вариант (как описано в статье, на которую я ссылаюсь), но если это ваш единственный вариант ...

http://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet

цитата из статьио том, что это последний параметр

Однако эта методология неэффективна по сравнению с использованием параметризованных запросов.Этот метод следует использовать только с осторожностью, чтобы модифицировать устаревший код экономически эффективным способом.Приложения, созданные с нуля, или приложения, требующие низкого допуска к риску, должны быть построены или переписаны с использованием параметризованных запросов.

По сути, аргумент против этого подхода даже в том случае, если вы избегаете всех известных неверных данных, нет никакой гарантии, что кто-то не найдет способ обойти его в будущем.

Однако, чтобы конкретно ответить на ваш вопрос ...

список символов, которые нужно экранировать, находится встатья, на которую я ссылаюсь выше.

Редактировать Как уже отмечалось, в статье не очень хорошие ссылки.Тем не менее, для SQL Server, это делает: http://msdn.microsoft.com/en-us/library/ms161953.aspx

Обратите внимание, что список символов, которые вы должны экранировать, будет варьироваться в зависимости от платформы БД, но похоже, что вы используете SQL Server, так что этодолжно быть уместно ..

Цитата из статьи ниже:

Фильтрация ввода также может быть полезна для защиты от внедрения SQL путем удаления управляющих символов.Однако из-за большого количества символов, которые могут создавать проблемы, это не надежная защита.В следующем примере выполняется поиск разделителя строк символов.

private string SafeSqlLiteral(string inputSQL)
{
  return inputSQL.Replace("'", "''");
}

Предложения LIKE

Обратите внимание, что при использовании предложения LIKE символы подстановки по-прежнему необходимо экранировать:

s = s.Replace("[", "[[]");
s = s.Replace("%", "[%]");
s = s.Replace("_", "[_]");
0 голосов
/ 25 октября 2017

Существует другой подход, который может возможно работать, хотя это зависит от того, какие символы разрешены в параметрах хранимой процедуры.Вместо того, чтобы избегать проблемных символов, которые можно использовать для внедрения SQL, удалите символы вместо этого.Например, если у вас есть этот SP:

create procedure dbo.MYSP(@p1 varchar(100))
as begin
  set @p1 = Replace(@p1, '''',' '); -- Convert single quotes to spaces
  set @p1 = Replace(@p1, ';', ' ');
  set @p1 = Replace(@p1, '--', ' ');      
  set @p1 = Replace(@p1, '/*', ' ');      
  set @p1 = Replace(@p1, '*/', ' ');      
  set @p1 = Replace(@p1, 'xp_', ' ');      
  ...
end;

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

0 голосов
/ 05 ноября 2010

Можете ли вы получить SQL CLR может быть очень полезным - вы можете, по крайней мере, использовать его для написания гораздо более эффективной дезинфекции, чем можно сделать с помощью T-SQL. В идеальном мире вы можете полностью заменить хранимые процессы на параметризованные операторы и другие более сильные структуры.

...