различия разрешений безопасности между select и sp_execute sql в хранимой процедуре - PullRequest
0 голосов
/ 30 января 2020

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

в том же контейнере хранимых процедур с тем же exe c разрешения для этого sp;

case 1: вызов sql строки запроса (select) с переменными, объединениями и т. д. c ...

case 2: построение sql строка запроса и вызов sp_execute sql с sql и всеми переданными в него параметрами

В ходе тестирования case 1 был значительно медленнее, чем case 2. Имеет смысл, все хорошо, потому что кеширование - это то, к чему мы стремились в этом отношении.

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

Вопросы //

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

Второе - ... Есть ли обходной путь ***?

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

пример кода варианта 1

         ts.tick_serv_id AS tick_serv_id
       ,ts.ticket_id AS ticket_id    
       ,ts.service_id AS service_id
       ,ts.quantity AS quantity
       ,ts.employee_id AS employee_id    
       ,ts.commission AS commission
       ,ts.assumed_service_date AS assumed_service_date
       ,ts.service_date AS service_date
       ,ts.status AS status
       ,ts.comments AS comments
       ,s.service_type_id AS service_type_id    
       ,st.description      AS description
       ,st.edit_quantity AS edit_quantity
       ,s.print_text AS print_text
       ,sr.unit_type AS unit_type
       ,sr.rate AS rate
       FROM ticket_services ts
   INNER JOIN tickets t ON t.ticket_id=ts.ticket_id
   INNER JOIN services s ON ts.service_id=s.service_id                                   
   INNER JOIN service_rates sr ON ts.service_id=sr.service_id 
   INNER JOIN service_types st ON s.service_type_id=st.service_type_id     
   WHERE (tick_serv_id = @tick_serv_id OR @tick_serv_id IS NULL)
     AND (ts.ticket_id = @ticket_id OR @ticket_id IS NULL)
       AND (ts.service_id = @service_id OR @service_id IS NULL)
       AND (ts.quantity = @quantity OR @quantity IS NULL)
       AND (ts.employee_id = @employee_id OR @employee_id IS NULL)
       AND (ts.commission = @commission OR @commission IS NULL)
       AND (ts.assumed_service_date = @assumed_service_date OR @assumed_service_date IS NULL)
       AND (ts.service_date = @service_date OR @service_date IS NULL)
       AND (t.open_date BETWEEN sr.open_date AND sr.close_date)
       AND (ts.status = @status OR @status = '')
       AND (ts.comments = @comments OR @comments = '')

пример кода варианта 2

      @tick_serv_id       bigint,
      @ticket_id            bigint,
      @service_id          int,
      @quantity            int,
      @employee_id         int,
      @commission          float,
      @assumed_service_date datetime,
      @service_date        datetime,
      @status              char(1),
      @comments            varchar(500)'

DECLARE @sql nvarchar(max) = N'
              SELECT
              ts.tick_serv_id 
              ,ts.ticket_id  
              ,ts.service_id  
              ,ts.quantity  
              ,ts.employee_id  
              ,ts.commission 
              ,ts.assumed_service_date 
              ,ts.service_date  
              ,ts.status  
              ,ts.comments 
              ,s.service_type_id  
              ,st.description      
              ,st.edit_quantity  
              ,s.print_text AS print_text
              ,sr.unit_type AS unit_type
              ,sr.rate AS rate
      FROM ticket_services ts
         INNER JOIN tickets t ON t.ticket_id=ts.ticket_id
      INNER JOIN services s ON ts.service_id=s.service_id                                       
      INNER JOIN service_rates sr ON ts.service_id=sr.service_id     
      INNER JOIN service_types st ON s.service_type_id=st.service_type_id  
         WHERE 1 = 1 AND (t.open_date BETWEEN sr.open_date AND sr.close_date)'
      + CASE WHEN @tick_serv_id IS NOT NULL THEN
        N' AND tick_serv_id = @tick_serv_id' ELSE N'' END
      + CASE WHEN @ticket_id IS NOT NULL THEN
        N' AND ts.ticket_id = @ticket_id' ELSE N'' END
      + CASE WHEN @service_id IS NOT NULL THEN
        N' AND ts.service_id LIKE @service_id' ELSE N'' END
      + CASE WHEN @quantity IS NOT NULL THEN
        N' AND ts.quantity = @quantity' ELSE N'' END
      + CASE WHEN @employee_id IS NOT NULL THEN
        N' AND ts.emplyee_id = @employee_id' ELSE N'' END
      + CASE WHEN @commission IS NOT NULL THEN
        N' AND ts.commission = @commission' ELSE N'' END
      + CASE WHEN @assumed_service_date IS NOT NULL THEN
        N' AND ts.assumed_service_date = @assumed_service_date' ELSE N'' END
      + CASE WHEN @service_date IS NOT NULL THEN
        N' AND ts.service_date = @service_date' ELSE N'' END
      + CASE WHEN @status <> '' THEN
        N' AND ts.status = @status' ELSE N'' END
      + CASE WHEN @comments  <> '' THEN
        N' AND comments = @comments' ELSE N'' END
         ;

              EXEC sys.sp_executesql @sql, @params,
              @tick_serv_id                                                
              ,@ticket_id                               
              ,@service_id                               
              ,@quantity                                                       
              ,@employee_id                                                 
              ,@commission                                             
              ,@assumed_service_date                    
              ,@service_date                            
              ,@status                                  
              ,@comments;   

1 Ответ

1 голос
/ 30 января 2020

Во-первых, почему ... существует ли конкретное c обоснование того, почему разрешения подразумеваются и каскадируются с одним, а не с другим

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

Во-вторых ... есть ли работа вокруг ***?

Dynami c SQL эффективно разрывает цепочку владения. Обходные пути, которые не требуют предоставления одного разрешения для конечных пользователей на объектах, включают EXECUTE AS и подписание pro c сертификатом, связанным с пользователем с необходимыми разрешениями.

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

CREATE CERTIFICATE YourStoredProcedureDynamicSqlCertificate
   ENCRYPTION BY PASSWORD = 'pGFD4bb925DGvbd2439587y'  
      WITH SUBJECT = 'Provide dynamic SQL permissions';   
GO
ADD SIGNATURE TO dbo.YourStoredProcedure
   BY CERTIFICATE YourStoredProcedureDynamicSqlCertificate 
    WITH PASSWORD = 'pGFD4bb925DGvbd2439587y';  
GO  
CREATE USER YourStoredProcedureDynamicSqlCertificateUser 
   FROM CERTIFICATE YourStoredProcedureDynamicSqlCertificate;  
GO  
GRANT SELECT ON dbo.services TO YourStoredProcedureDynamicSqlCertificateUser;  
GRANT SELECT ON dbo.tickets TO YourStoredProcedureDynamicSqlCertificateUser;  
GO  
ALTER CERTIFICATE YourStoredProcedureDynamicSqlCertificate
    REMOVE PRIVATE KEY;
GO
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...