应用场景:
作为SQL Server安全审计的一部分,DBA可能需要这样的一份报吿:知道哪些数据库对象(储如表、存储过程、视图、用户、函数、用户权限等等)在什么时候被谁修改过,以及修改的内容等等。也还可能需要一份存储过程、函数以及视图的代码修改历史清单,知道这些代码在去过某段时间里被修改。那么,下面这份代码正好是你所需要的。
此代码在SQL Server 2005/2008运行良好。
1. 建立一个审计数据库,专门用于存放审计记录;
此步骤略。审计数据库名为:AuditDB;
2. 建立一个审计用户,用于实施审计时所需要的权限;
- CREATE LOGIN[AuditUser]WITH PASSWORD=N'123',DEFAULT_DATABASE=[AuditDB]
- GO
- use AuditDB
- GO
- CREATE USERAudituserFOR LOGIN Audituser;
- GO
- ALTER LOGIN[AuditUser]DISABLE
- GO
注:该用户无需登录,所以可以禁用此用户的登录。
3. 在审计数据库AuditDB上建立下如下审计表:
表1:DBA_ChangedObjectLog(审计主表)
表2:DBA_ChangedObjectText(审计从表)
注:
审计主表与审计从表之间的关联字段是LogID;
只有视图、存储过程、函数、触发器才有从表数据,因为只有这些对象才有代码定义;
4. 为审计用户AuditUser授权:
- use master
- go
- grant viewserverstate to Audituser
- use AuditDB
- go
- grant insertonDBA_ChangedObjectLogtoAudituser
- grant insertonDBA_ChangedObjectTexttoAudituser
5. 为所有可以修改数据库对象的用户,授予模拟AuditUser的权限:
如果你不这么做,那你就必须为所有这些用户分别授予第四步中AuditUser的权限;
- grant impersonateonlogin::user1toaudituser
- grant impersonateonlogin::auditusertouser1
如果你的数据库中已经有几十上百或上千的用户,那你可以用如下语句生成的代码批量授权:
- select 'grant impersonate on login::['+[name]+'] to audituser'fromsys.server_principals
- where type='S'andname<>'audituser'
- union all
- select 'grant impersonate onlogin::audituser to ['+[name]+']'fromsys.server_principals
- where type='S'andname<>'audituser'
注:以对后新建的每个用户,也必须这样授权。
6. 在审计主表上建立触发器tr_Dba_ObjectChangeLog_insert。
此触发器的目的是获得当前修改的数据库对象(视图、存储过程、函数、触发器)代码定义并保存入从表中。
- USE [AuditDB]
- GO
- SET ANSI_NULLSON
- GO
- SET QUOTED_IDENTIFIERON
- GO
- --在审计主表上建立触发器:
- ALTER trigger[dbo].[tr_Dba_ObjectChangeLog_insert]
- on [dbo].[DBA_ChangedObjectLog]
- for insert
- as
- begin
- declare
- @Logid uniqueidentifier,
- @DatabaseNamenvarchar(256),
- @UserNamenvarchar(256),
- @SchemaNamenvarchar(256),
- @ObjectNamenvarchar(256),
- @ObjectTypenvarchar(256),
- --@original_usernvarchar(256),
- @EventTypenvarchar(256),
- @sql nvarchar(256),
- @IsaDDLTriggerint
- execute as login =original_login()
- set @IsaDDLTrigger= -1
- select @Logid=logid,@DatabaseName=DatabaseName,@SchemaName=SchemaName,@ObjectName=ObjectName,@ObjectType=ObjectType,@UserName=UserName,@EventType=EventType
- from inserted;
- if @ObjectType not in('PROCEDURE','VIEW','FUNCTION','TRIGGER') return;
- if @EventType like 'DROP%'return;
- set @sql = N'select @IsaDDLTriggerOut=count(*) from ['+@DatabaseName+'].sys.triggers where name='''+@ObjectName+''' and parent_class=0';
- execute sp_executesql@sql,N'@IsaDDLTriggerOut int OUTPUT',@IsaDDLTriggerOut=@IsaDDLTriggerOUTPUT;
- --DDL触发器不能使用sp_helptext获得它的文本
- if @IsaDDLTrigger>0 return;
- if object_id('tempdb..#temp')isnot null
- drop table #temp
- create table #temp(
- [Line][int]IDENTITY(1,1)NOTNULL,
- [LineText]varchar(max)
- )
- set @sql='insert into #temp exec ['+@DatabaseName+'].dbo.sp_helptext '''+@SchemaName+'.'+@ObjectName+'''';
- exec (@sql)
- execute as caller
- insert into dbo.Dba_ChangedObjectText
- select @Logid,@DatabaseName,@SchemaName,@ObjectName,[Line],[LineText]from#temp
- drop table #temp
- end
7. 在要实施审计的数据库上建立触发器tr_dba_Trace_ObjectChangeLog:
建立之前,需要对要实施审计的数据库如下配置。例如你需要对数据库TEST进行DDL审计,则:
- alter database[TEST]set trustworthy on
然后再建立触发器tr_dba_Trace_ObjectChangeLog
- USE [TEST]
- GO
- SET ANSI_NULLSON
- GO
- SET QUOTED_IDENTIFIERON
- GO
- CREATE trigger[tr_dba_Trace_ObjectChangeLog]
- on database
- --withexecute as 'AuditUser'
- for ddl_table_events,ddl_view_events,ddl_index_events,
- ddl_synonym_events,ddl_function_events,ddl_procedure_events,ddl_trigger_events,
- ddl_database_security_events--,ddl_rule_events
- as
- begin
- set nocount on
- declare
- @newid UNIQUEIDENTIFIER,
- @data xml,
- @spid smallint,
- @LoginNamevarchar(256),
- @ProgramNamevarchar(256),
- @IP varchar(15),
- @mac varchar(12),
- @EventTypevarchar(50),
- @ObjectTypevarchar(25),
- @ObjectNamevarchar(256),
- @IsaDDLTriggerint,
- @Line smallint
- set @newid = newid()
- set @data = EVENTDATA()
- set @spid = @data.value('(/EVENT_INSTANCE/SPID)[1]','smallint')
- set @EventType = @data.value('(/EVENT_INSTANCE/EventType)[1]','varchar(50)')
- set @ObjectType = @data.value('(/EVENT_INSTANCE/ObjectType)[1]','varchar(25)')
- set @ObjectName = @data.value('(/EVENT_INSTANCE/ObjectName)[1]','varchar(256)')
- set @LoginName = @data.value('(/EVENT_INSTANCE/LoginName)[1]','varchar(256)')
- --set @ObjectName= @data.value('(/EVENT_INSTANCE/ObjectName)[1]', 'varchar(256)')
- set @mac =(SELECTNET_ADDRESSFROM master.dbo.sysprocessesWHEREspid = @spid)
- set @Line = 0
- --过滤掉由维护计划生成的重建索引记录(因此类记录太庞大)
- if @LoginName='NT AUTHORITY\SYSTEM'and@ObjectType= 'INDEX'
- return;
- -- if the objectis a ddl trigger
- if (selectcount(*)from sys.triggerswherename=@ObjectNameandparent_class=0)= 0
- set @IsaDDLTrigger = 0
- else set @IsaDDLTrigger = 1
- execute as login ='AuditUser'
- select @ProgramName=[Program_Name]fromsys.dm_exec_sessionswheresession_id=@spid
- select @IP=client_net_addressfromsys.dm_exec_connectionswheresession_id=@spid
- insert into DBAdminPlat.dbo.DBA_ChangedObjectLog(
- [LogId],
- [EventType],[PostTime],[SPID],[ServerName],[Host_IP_Address],[Host_MAC_Address],
- [ProgramName],[LoginName],[UserName],[DatabaseName],[SchemaName],
- [ObjectName],[ObjectType],[TSQLCommand]--,[EventDate]
- )
- values(
- @newid,
- @EventType,
- @data.value('(/EVENT_INSTANCE/PostTime)[1]','datetime'),
- @spid,
- @data.value('(/EVENT_INSTANCE/ServerName)[1]','varchar(256)'),
- @IP,
- @mac,
- @ProgramName,
- @LoginName,--@data.value('(/EVENT_INSTANCE/LoginName)[1]','varchar(256)'),
- @data.value('(/EVENT_INSTANCE/UserName)[1]','varchar(256)'),
- @data.value('(/EVENT_INSTANCE/DatabaseName)[1]','varchar(256)'),
- @data.value('(/EVENT_INSTANCE/SchemaName)[1]','varchar(256)'),
- @ObjectName,
- @ObjectType,
- --For objectslike procs, views, triggers and functions ,text of which will be stored inmaster.[dbo].[tb_dba_ChangedObjectText] table.
- case when @ObjectType in('PROCEDURE','VIEW','FUNCTION','TRIGGER')and(@EventType<>'GRANT_DATABASE'or@EventType like 'DROP%')and@IsaDDLTrigger=0
- then null else @data.value('(/EVENT_INSTANCE/TSQLCommand)[1]','varchar(max)')end
- );
- REVERT;
- end
- GO
- SET ANSI_NULLSOFF
- GO
- SET QUOTED_IDENTIFIEROFF
- GO
- ENABLE TRIGGER[tr_dba_Trace_ObjectChangeLog]ON DATABASE
- GO