zoukankan      html  css  js  c++  java
  • 数据库级别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
    View Code

    【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
  • 相关阅读:
    MVC3 Razor模板引擎
    Razor引擎学习:RenderBody,RenderPage和RenderSection
    Lambda表达式详解
    MVC的重定向页面的跳转
    dataSet==>Ilist<>的函数封装
    shell 判断目录还是文件
    大写金额转小写(千万以下)
    python将有序列表转乱序,模拟音乐播放器随机播放列表
    ssh登录远程linux服务器的错误
    ubuntu Unable to locate package错误解决办法
  • 原文地址:https://www.cnblogs.com/gered/p/10577509.html
Copyright © 2011-2022 走看看