Динамический TSQL в безопасном контексте - PullRequest
1 голос
/ 13 октября 2010

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

UPDATE [Table] SET [Column 1] = @Column1 

Но в этой ситуации [Столбец 1] и его значение будет установлено во время выполнения.

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

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

Ответы [ 4 ]

5 голосов
/ 13 октября 2010

Единственный способ динамически определить имя столбца в TSQL - использовать конкатенацию строк:

DECLARE @SQL = 'UPDATE [your_table] 
                   SET '+ @ColumnName + ' = @ColumnValue '

BEGIN

  EXEC sp_executesql @SQL, 
                     N'@ColumnValue INT',
                     @ColumnValue        

END

... потому что, если вы использовали:

DECLARE @SQL = 'UPDATE [your_table] 
                   SET @ColumnName = @ColumnValue '

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

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

Альтернатива для рассмотрения

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

3 голосов
/ 13 октября 2010

В настоящее время ваши процедуры работают из-за цепочки владения :

Когда к объекту обращаются через цепочку, SQL Server сначала сравнивает владельца объекта с владельцем объекта.вызывающий объект.Это предыдущая ссылка в цепочке.Если оба объекта имеют одного и того же владельца, разрешения для указанного объекта не оцениваются.

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

  • добавьте в процедуру предложение EXECUTE AS CALLER
  • создайте сертификат вв базе данных
  • подписать процедуру
  • сбросить закрытый ключ сертификата (сверху предотвратить дальнейшее использование)
  • создать пользователя, полученного из сертификата
  • grantтребуемая привилегия (SELECT / UPDATE / DELETE / INSERT для таблицы ) для пользователя, полученного из сертификата

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

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

0 голосов
/ 13 октября 2010

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

Используйте ISNULL(), а в операторе UPDATE используйте текущие значения строки. COALESCE также может быть использовано.

CREATE PROC UpdateCustomer

@ID   int,
@FirstName varchar(100),
@LastName varchar(100),
@LastLoginTime smalldatetime

AS

UPDATE Customer
SET    FirstName = ISNULL(@FirstName,FirstName)
      ,LastName = ISNULL(@LastName ,LastName)
      ,LastLoginDate = ISNULL(@LastLoginTime ,LastLoginDate )
WHERE ID = @ID

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

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

0 голосов
/ 13 октября 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...