将用户添加到数据库时的安全触发器

Security trigger when user is added to a database

如何在 SQL 服务器中实现触发机制,执行特定操作,例如在将新用户添加到数据库时发送电子邮件。

想法是,有一个数据仓库,每个人,即 IT、性能团队、DW 团队都可以访问并能够将用户添加到数据库。

现在的问题是,我们希望用户在被授予使用我们数据库的权限之前联系我们的部门经理。

目前,我们通过展开“安全”>“用户”部分来进行日常检查,以查看列出的用户是否是预期的用户,这会带来一个问题,即如果添加了用户并进行了查询,然后将其删除了会怎样.

可以使用 MS SQL Profiler,但是,它仅用于查询审核,而不适用于有权访问的人(如果 Profiler 中有此方法,请告诉我)。

在我看来,可能是编写一个 PowerShell 脚本,让它每分钟随机执行一次,看看是否有什么不同,然后通过电子邮件发送。然而,这个想法似乎滥用了服务器性能。请提前指教,谢谢。

您可以使用 DDL 事件创建 DDL 触发器作为 CREATE USER,您可以相应地发送电子邮件。您可以创建邮件配置文件并使用 sp_send_dbmail

发送邮件
CREATE TRIGGER NewUserAdditionAlert
ON DATABASE   
FOR CREATE_USER  
AS   
BEGIN
DECLARE @create_user_stmt NVARCHAR(2000) = (SELECT EVENTDATA().value  
        ('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]','nvarchar(max)'));

EXEC msdb.dbo.sp_send_dbmail @recipients='test@test.com',  
    @subject = 'New User is trying to get created',  
    @body = @create_user_stmt ;
   
   RAISERROR ('You cannot create user in database. Contact test@test.com for getting access to database!', 10, 1)  
   
   ROLLBACK  
END
GO  

感谢 Venkataraman (https://whosebug.com/users/634935/venkataraman-r) 建议创建一个可能会中断用户创建过程的 DDL 触发器。 DDL 触发器说明可以在 Microsoft 文档页面和/或现代数据库管理第 13 版书籍的 DDL 触发器部分(Hoffer、Ramesh 和 Topi,2022 年)找到,请查看此内容的末尾以找到这些链接和详细信息。 根据建议,我使用以下 SQL 查询创建了一个服务器级 DDL 触发器,并测试了在 [] > Security > Logins.

上创建用户

触发器起作用并阻止我在服务器级别添加用户,这显示了以下错误。

但是,由于我希望它在特定数据库上工作,所以使用上述方法并不是真正的解决方案。因此,我将查询的“所有服务器”部分更改为“数据库”。但是,这引发了一个错误,指出“指定的事件类型 is/are 在指定的目标对象上无效”,请参阅下面的屏幕截图了解更多详细信息。

下一个测试是稍微更改查询以删除上述查询,使用“在所有服务器上删除触发器users_security_trigger”,然后为数据库创建一个触发器并将“create_login”替换为“create_user”,如下图所示

此查询执行成功并阻止我创建用户,如下面的屏幕截图所示。

这也将阻止具有权限的用户尝试将“用户映射”添加到数据库,因此,此查询按预期工作。

邮箱配置:

/*
    -- activate database email
    sp_configure 'Database Mail XPs', 1;
    go
    reconfigure
    go
*/

-- fill out the below section for the email account
declare @email_address nvarchar(128) = N'your email address';
declare @replyto_address nvarchar(128) = N'your reply to email address';
declare @password nvarchar(128) =  N'your email password';

-- leave the below if using Microsoft
declare @mailserver_name nvarchar(128) = N'smtp.office365.com'; -- for Microsoft default "smtp.office365.com"
declare @mailserver_type nvarchar(128) = N'SMTP'; -- for Microsoft default "SMTP"
declare @port int = 587; -- for Microsoft default port 587

-- you can leave the below as default
declare @enable_ssl bit = 1; -- default = 1
declare @description nvarchar(256) = concat(N'Email used for sending outgoing reports using "', @email_address, '"') ;


-- create profile, account and profile account
/* 1. create profile */ 
if not exists(
select * from msdb.dbo.sysmail_profile where  name = @email_address
) begin exec msdb.dbo.sysmail_add_profile_sp @email_address,  @description; end;

/* 2. create account */ 
if not exists(
select * from msdb.dbo.sysmail_account where  name = @email_address
) begin 
exec msdb.dbo.sysmail_add_account_sp 
@email_address, @email_address, @email_address, @replyto_address, @description,
@mailserver_name, @mailserver_type, @port, @email_address, @password, 0, @enable_ssl,null;
end;
    
/* 3. create account profile */ 
if not exists(
select * from msdb.dbo.sysmail_profileaccount pa  
    inner join msdb.dbo.sysmail_profile p on pa.profile_id = p.profile_id  
    inner join msdb.dbo.sysmail_account a on pa.account_id = a.account_id   
    where p.name = @email_address and a.name = @email_address
) begin  
exec msdb.dbo.sysmail_add_profileaccount_sp null,@email_address, null, 
@email_address, @sequence_number =1; 
end;

/* 4. check if profile is created */ exec msdb.dbo.sysmail_help_profile_sp;
/* 5. check if account is created */ exec msdb.dbo.sysmail_help_account_sp;
/* 6. check if profile account is created */ exec msdb.dbo.sysmail_help_profileaccount_sp;

/*
-- deletion if needed, uncomment as necessary

/* 1. delete profile account */ 
if exists(
    select * from msdb.dbo.sysmail_profileaccount pa  
    inner join msdb.dbo.sysmail_profile p on pa.profile_id = p.profile_id  
    inner join msdb.dbo.sysmail_account a on pa.account_id = a.account_id   
    where p.name = @email_address and a.name = @email_address
) begin  exec msdb.dbo.sysmail_delete_profileaccount_sp  null, @email_address; end;

/* 2. delete account */ 
if exists(
select * from msdb.dbo.sysmail_account where  name = @email_address
) begin exec msdb.dbo.sysmail_delete_account_sp  null, @email_address; end;

/* 3. delete profile*/
if exists(
select * from msdb.dbo.sysmail_profile where  name = @email_address
) begin exec msdb.dbo.sysmail_delete_profile_sp null, @email_address; end;
*/

-- send a test email
exec msdb.dbo.sp_send_dbmail
@profile_name = <@email_address ‘your email address’>,
@recipients = 'send to address',
@subject = 'Automated message',
@body = 'email successfully configured';

go

最后,当来自 IT、DW 或任何有权在您的数据库上添加/创建用户的用户时,您可以使用以下查询。此查询不会中断添加用户,它只会通过电子邮件通知您。

use [database]
go

alter trigger users_security_trigger on database for create_user as 
begin

declare @body varchar(500)= 
'User ' +
lower(quotename(eventdata().value('(/EVENT_INSTANCE/ObjectName)[1]', 'varchar(300)'))) + 
' was added to your database by ' + 
lower(quotename(system_user));

exec msdb.dbo.sp_send_dbmail
@profile_name = <@email_address ‘your email address’>,
@recipients = 'send to address',
@subject = 'A new user was created on DRS database',
@body = @body;

end;

参考资料

  1. Hoffer, J.、Ramesh, V. 和 Topi, H.,2022。现代数据库管理。第 13 版。英国哈洛:Pearson Education Limited,第 281-284 页。
  2. Microsoft 文档页面(DDL 触发器 https://docs.microsoft.com/en-us/sql/relational-databases/triggers/ddl-triggers?view=sql-server-ver16
  3. https://dba.stackexchange.com/questions/228577/how-to-deny-permissions-for-newly-created-user-in-ddl-trigger
  4. https://www.sqlshack.com/configure-database-mail-sql-server/
  5. https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/database-mail-stored-procedures-transact-sql?view=sql-server-ver16