Como filtrar o uso da Função Definida pelo Usuário com Valor Escalar dos Dados de Auditoria do SQL Server?

12

Temos um banco de dados SQL Server que possui uma especificação de auditoria de banco de dados que audita todas as ações de execução no banco de dados.

CREATE DATABASE AUDIT SPECIFICATION [dbAudit]
FOR SERVER AUDIT [servAudit]
ADD (EXECUTE ON DATABASE::[DatabaseName] BY [public])

Descobrimos que algumas consultas gravam no log de auditoria o uso de uma função escalar para cada linha de um conjunto de resultados. Quando isso acontece, o registro é preenchido antes que possamos colocá-lo em ETL em seu local de descanso final e temos uma lacuna em nosso registro.

Infelizmente, devido a motivos de conformidade, não podemos simplesmente parar de auditar todas as EXECUTEdeclarações.

Nosso primeiro pensamento para abordar esse problema é usar a WHEREcláusula na Auditoria do Servidor para filtrar a atividade. O código ficou assim:

WHERE [object_id] not in (Select object_id from sys.objects where type = 'FN' )

Infelizmente, o SQL Server não permite o operador IN relacional (provavelmente porque não deseja consultar toda vez que precisar gravar no log de auditoria).

Gostaríamos de evitar escrever um processo armazenado que codifique a cláusula object_idna WHEREcláusula, mas esse é o nosso pensamento atual sobre a melhor maneira de abordar esse problema. Existe uma abordagem alternativa que devemos considerar?

Percebemos que, quando a função escalar está em uso em um CTE recursivo, ela faz com que a consulta grave no log de auditoria de todas as linhas do conjunto de resultados.

Existem algumas funções avaliadas escalares que são entregues por um fornecedor que não podemos excluir ou mover para um banco de dados alternativo.

Mark Iannucci
fonte
6
We've found that some queries will write to the audit log the use of a scalar function for every row in a result set.- Esse é um dos efeitos colaterais mais magníficos das UDFs escalares que eu já ouvi e ouvi muito.
Erik Darling
3
Existe uma opção de criar UDFs que você não deseja que sejam auditados em um banco de dados separado (que não seja auditado) e invocá-los via nome de 3 partes?
Scott Hodgin
@ScottHodgin, eu gosto da solução alternativa, mas em nossas circunstâncias existem algumas funções de valor escalar que são entregues por um fornecedor que não podemos excluir ou mover para um banco de dados alternativo.
22318 Mark Iannucci
Os seguintes podem estar se perguntando que caso faz com que a consulta grave no log de auditoria para cada linha de um conjunto de resultados; notamos que isso ocorre quando a função escalar está em uso em um CTE recursivo.
Mark Iannucci

Respostas:

6

Existem algumas opções que consegui trabalhar. Todas as opções lidam com variações de predicados de filtro. NOTA: você deve desativar o Audit Server, a fim de fazer alterações e, em seguida, re- habilitar -lo.

Primeiro, a abordagem mais genérica é filtrar todas as UDFs escalares. Você pode fazer isso usando o class_typecampo de auditoria. A documentação indica que esse campo é VARCHAR(2), mas não permite especificar uma sequência. No entanto, consegui o seguinte para trabalhar:

ALTER SERVER AUDIT [servAudit]
WHERE ([class_type] <> 20038); -- EXECUTE Scalar UDF

(mais informações sobre essa investigação aqui: Server Audit Mystery: Filtering class_type obtém a mensagem de erro 25713 )

A próxima abordagem mais genérica não é uma opção, pois foi declarado que este é um banco de dados fornecido pelo fornecedor e, portanto, nenhuma alteração pode ser feita. Então eu vou cobrir isso por último.

A abordagem menos genérica (mas definitivamente funciona) é filtrar o nome da função específica:

ALTER SERVER AUDIT [servAudit]
WHERE ([object_name]<>'function_name');

Ou, se vários nomes:

ALTER SERVER AUDIT [servAudit]
WHERE ([object_name]<>'function_name1' AND [object_name]<>'function_name2');

Embora não seja muito genérica, essa abordagem deve ser adequada, pois o número de funções a serem filtradas deve ser bastante pequeno e não será muito frequente a introdução de novas funções.

Finalmente, para outras pessoas que enfrentam essa situação e não estão impedidas de fazer alterações: você pode colocar funções em seu próprio esquema e filtrar apenas esse esquema. Isso é mais genérico do que filtrar as funções individualmente. Supondo que você crie um esquema nomeado fne coloque as funções nele:

ALTER SERVER AUDIT [servAudit]
WHERE ([schema_name]<>'fn');

TAMBÉM, com relação aos dois comentários a seguir na pergunta:

Infelizmente, o SQL Server não permite o operador IN relacional (provavelmente porque não deseja consultar toda vez que precisar gravar no log de auditoria).

e:

Gostaríamos de evitar escrever um processo armazenado que codifique o objeto_id na cláusula WHERE

O INoperador não é o problema. É verdade que não é suportado, mas é apenas uma abreviação de uma lista de ORcondições. O problema real é o uso do T-SQL. Somente literais - strings ou números - são permitidos. Portanto, você não seria capaz de executar um Procedimento Armazenado de qualquer maneira. Nem você pode usar as funções internas.

Solomon Rutzky
fonte
obrigado por esta resposta. Estamos no processo de implementação dessa alteração e aceitarei essa resposta quando confirmarmos que ela funciona em nosso ambiente.
precisa saber é o seguinte
1
@MarkIannucci Thanks! Além disso, consertei um pequeno bug na minha sugestão ideal. Eu tinha copiado e colado do teste onde estava filtrando as funções FOR em vez de QUALQUER COISA, mas funções. Eu mudei o =para estar <>na minha resposta. Eu também só testei e funciona como anunciado :-)
Solomon Rutzky