数据库级别DDL操作监控审计、数据库触发器/服务器触发器
关键词:数据库触发器/服务器触发器 ,数据库级别DDL操作监控审计,禁止修改登录名密码,登录触发器,login触发器
【1】数据库级别DDL操作监控审计
SQL Server 2005开始支持DDL触发器,它不只限于对CREATE/ALTER/DROP操作有效,支持的DDL事件还有比如:权限的GRANT/DENY/REVOEK, 对象的RENAME, 更新统计信息等等,可通过DMV查看更多支持的事件类型如下:
select * from sys.trigger_event_types where type_name not like '%CREATE%' and type_name not like '%ALTER%' and type_name not like '%DROP%'
注意:
1. TRUNCATE不在DDL触发器的事件类型中,SQL Server中将Truncate 归为DML操作语句,虽然它也并不触发DML触发器,就像开启开关的大批量导入操作 (Bulk Import Operations) 一样;
2. DDL触发器中捕获的信息都由EVENTDATA()函数返回,返回类型为XML格式,需要用XQuery来读取;
案例:转自2012示例库,只能数据库级别,不能实例级别
use database
go
SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO
create table databaseLog( [PostTime] datetime,
[DatabaseUser] varchar(500),
[Event] varchar(500),
[Schema] varchar(50),
[Object] varchar(4000),
[TSQL] varchar(4000),
[XmlEvent] xml)
CREATE TRIGGER [ddlDatabaseTriggerLog] ON DATABASE --all server 实例级别 FOR DDL_DATABASE_LEVEL_EVENTS AS --DDL_SERVER_LEVEL_EVENTS 实例级别 BEGIN SET NOCOUNT ON; DECLARE @data XML; DECLARE @schema sysname; DECLARE @object sysname; DECLARE @eventType sysname; SET @data = EVENTDATA(); SET @eventType = @data.value('(/EVENT_INSTANCE/EventType)[1]', 'sysname'); SET @schema = @data.value('(/EVENT_INSTANCE/SchemaName)[1]', 'sysname'); SET @object = @data.value('(/EVENT_INSTANCE/ObjectName)[1]', 'sysname') IF @object IS NOT NULL PRINT ' ' + @eventType + ' - ' + @schema + '.' + @object; ELSE PRINT ' ' + @eventType + ' - ' + @schema; IF @eventType IS NULL PRINT CONVERT(nvarchar(max), @data); INSERT [dbo].[DatabaseLog] ( [PostTime], [DatabaseUser], [Event], [Schema], [Object], [TSQL], [XmlEvent] ) VALUES ( GETDATE(), CONVERT(sysname, CURRENT_USER), @eventType, CONVERT(sysname, @schema), CONVERT(sysname, @object), @data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(max)'), @data ); END; GO SET ANSI_NULLS OFF GO SET QUOTED_IDENTIFIER OFF GO
--开启/关闭 ENABLE TRIGGER [ddlDatabaseTriggerLog] ON DATABASE DISABLE TRIGGER [ddlDatabaseTriggerLog] ON DATABASE GO
--删除
DROP TRIGGER tri_LogServerEvent ON DATABASE;
--添加扩展属性到数据库对象中(即添加数据字典注解)
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'Database trigger to audit all of the DDL changes made to the AdventureWorks2008R2 database.' ,
@level0type=N'TRIGGER',@level0name=N'ddlDatabaseTriggerLog' GO
禁止特定角色的用户对特定的表做DROP操作
IF exists(select * from sys.triggers where name = 'NO_DROP_TABLE' and parent_class_desc = 'DATABASE') DROP TRIGGER [NO_DROP_TABLE] ON DATABASE; GO CREATE TRIGGER NO_DROP_TABLE ON DATABASE FOR DROP_TABLE AS BEGIN DECLARE @x XML, @user_name varchar(100), @db_name varchar(100), @schema_name varchar(100), @object_name varchar(200) --select eventdata() SET @x = EVENTDATA(); SET @user_name = @x.value('(/EVENT_INSTANCE/UserName)[1]','varchar(100)'); SET @db_name = @x.value('(/EVENT_INSTANCE/DatabaseName)[1]','varchar(100)'); SET @schema_name = @x.value('(/EVENT_INSTANCE/SchemaName)[1]','varchar(100)'); SET @object_name = @x.value('(/EVENT_INSTANCE/ObjectName)[1]','varchar(100)'); --PRINT 'Current User: ' + @user_name --PRINT 'Current Database: ' + @db_name --PRINT 'Schema Name: ' + @schema_name --PRINT 'Table Name: ' + @object_name IF is_rolemember('disallow_modify_tables',@user_name) = 1 AND @db_name = 'YOUR_DB_NAME' AND @schema_name = 'YOUR_SCHEMA_NAME' AND @object_name like 'YOUR_TABLE_NAME%' BEGIN PRINT 'Dropping tables is not allowed' ROLLBACK END END GO
【2】数据库触发器/服务器触发器
禁止修改登录名密码
create table db_del..check_login(xml_str xml);
GO
CREATE TRIGGER tr_alterpwd ON all server FOR ALTER_LOGIN AS BEGIN insert into db_del..check_login values( EVENTDATA()) RAISERROR('搞事情,敢改我密码',16,1) ROLLBACK END
【3】登录触发器( LOGON 触发器)
SQL Server 2005在SP2中悄悄引入了LOGON触发器,作为一个实例级的对象,它的系统视图,定义语句和DDL/DML触发器都是分开的。
select * from sys.server_triggers where name = 'login_history_trigger' select * from sys.server_trigger_events select OBJECT_ID('login_history_trigger') --无法获取
在SQL Server中,顾名思义,LOGON触发器,只支持LOGON事件;
在ORACLE中,实例级触发器可支持更多事件 (SERVERERROR, LOGON, LOGOFF, STARTUP, or SHUTDOWN)。
代码示例1: 记录所有login登录历史
(其实也可以通过修改login auditing选项,来记录成功和失败的登录在errorlog里)
IF OBJECT_ID('login_history','U') is not null DROP TABLE login_history GO CREATE TABLE login_history ( FACT_ID bigint IDENTITY(1,1) primary key, LOGIN_NAME nvarchar(1024), LOGIN_TIME datetime ) GO IF EXISTS(select 1 from sys.server_triggers where name = 'login_history_trigger') DROP TRIGGER login_history_trigger ON ALL SERVER GO CREATE TRIGGER login_history_trigger ON ALL SERVER FOR LOGON AS BEGIN --IF SUSER_NAME() NOT LIKE 'NT AUTHORITY\%' AND -- SUSER_NAME() NOT LIKE 'NT SERVICE\%' IF ORIGINAL_LOGIN() NOT LIKE 'NT AUTHORITY\%' AND ORIGINAL_LOGIN() NOT LIKE 'NT SERVICE\%' BEGIN INSERT INTO DBA..login_history VALUES(ORIGINAL_LOGIN(),GETDATE()); END; END; GO --view login history after logon SELECT * FROM login_history
代码示例2: 限制特定用户在特定时间范围登录、限制连接数
--限制下班时间不能登录 DROP TRIGGER IF EXISTS limit_user_login_time ON ALL SERVER GO CREATE TRIGGER limit_user_login_time ON ALL SERVER FOR LOGON AS BEGIN IF ORIGINAL_LOGIN() = 'TestUser' AND (DATEPART(HOUR, GETDATE()) < 9 OR DATEPART (HOUR, GETDATE()) > 18) BEGIN PRINT 'TestUser can only login during working hours!' ROLLBACK END END GO --限制连接数 DROP TRIGGER IF EXISTS limit_user_connections ON ALL SERVER GO CREATE TRIGGER limit_user_connections ON ALL SERVER WITH EXECUTE AS 'sa' FOR LOGON AS BEGIN IF ORIGINAL_LOGIN() = 'TestUser' AND (SELECT COUNT(*) FROM sys.dm_exec_sessions WHERE Is_User_Process = 1 AND Original_Login_Name = 'TestUser') > 2 BEGIN PRINT 'TestUser can only have 1 active session!' ROLLBACK END END
注意:如果LOGON触发器把所有人都锁在外面了怎么办?
Logon failed for login 'TestUser' due to trigger execution.
这时,只能通过DAC登录SQL Server去禁用LOGON触发器/修改逻辑以允许登录,DAC登录方式有远程和本地两种,远程登录需要通过sp_configure 开启remote admin connections ,如果没有事先开启,那就只能选择本地登录方式:
服务器本地,在SSMS中通过DAC登录
服务器本地,在cmd中通过DAC登录
--禁用/启用LOGON触发器 DISABLE TRIGGER limit_user_connections ON ALL SERVER ENABLE TRIGGER limit_user_connections ON ALL SERVER