Как очистить динамический SQL в SQL Server-предотвращение SQL-инъекции

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

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

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

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

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

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

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

есть ли набор символов, которые мы можем отфильтровать, чтобы убедиться, что мы не восприимчивы к SQL-инъекции?

8 ответов


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

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

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

дополнительные сведения см. В разделе 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-плюс все переменные результата.


тривиальные случаи могут быть исправлены QUOTENAME заменить:

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

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

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

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

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


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

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

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

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

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

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

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

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


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

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 ''' 

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

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

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

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

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

список символов для escape находится в статье, с которой я связан выше.

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

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

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

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

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

как П.

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

s = s.Replace("[", "[[]");
s = s.Replace("%", "[%]");
s = s.Replace("_", "[_]");

есть ли набор символов, которые мы можем отфильтровать, чтобы убедиться, что мы не восприимчивы к SQL-инъекции?

нет

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


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


есть другой подход, который мая возможно работать, хотя это зависит от того, какие символы разрешены в параметрах хранимой процедуры. Вместо того чтобы избегать проблемных символов, которые могут использоваться для 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;

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

обратите внимание, что набор замененных символов основан наhttps://msdn.microsoft.com/en-us/library/ms161953 (SQL.105).aspx