SQL Server登陸賬戶、數據庫用戶(包含用戶)及權限的自動化審計

登陸賬戶及權限變更的審計

因爲系統視圖sys.server_permissions沒有記錄登陸賬戶權限變更的時間,同時爲了更及時、全面的瞭解到登陸賬戶及權限變更的信息,保障數據庫的安全,使用DDL觸發器事件 DDL_LOGIN_EVENTS、DDL_GDR_SERVER_EVENTS、ADD_SERVER_ROLE_MEMBER、ALTER_SERVER_ROLE、CREATE_SERVER_ROLE(後面兩個事件適用於SQL Server 2012及以上版本)記錄登陸賬戶的變更信息,並郵件通知管理人員。

下面先創建登陸賬戶變更信息記錄表:

USE master
GO
CREATE TABLE dbo.loginAudit_log
    (
     id INT IDENTITY(1, 1)
    ,Posttime DATETIME NULL
    ,Servername VARCHAR(30) NULL
    ,Hostname VARCHAR(30) NULL
    ,Loginame VARCHAR(30) NULL
    ,Logintype VARCHAR(20) NULL
    ,ClientHost VARCHAR(20) NULL
    ,DDLType VARCHAR(30) NULL
    ,Grantor SYSNAME NULL
    ,Grantee SYSNAME NULL
    ,Permission VARCHAR(30) NULL
    ,ObjectName SYSNAME NULL
    ,ObjectType VARCHAR(30) NULL
    ,GrantOption BIT NULL
    ,CascadeOption BIT NULL
    ,TSQLCommand NVARCHAR(800) NULL
    )

接着創建服務器及觸發器,記錄並警告登陸賬戶及權限變更:

USE master
go
CREATE TRIGGER tr_LoginPermission_Audit ON ALL SERVER
    WITH EXECUTE AS 'sa'
    FOR DDL_LOGIN_EVENTS, DDL_GDR_SERVER_EVENTS,ADD_SERVER_ROLE_MEMBER
    --,ALTER_SERVER_ROLE,CREATE_SERVER_ROLE     --適用於SQL Server 2012及以上版本
AS
BEGIN 
    DECLARE @Data XML 
    DECLARE @PostTime NVARCHAR(24) 
    DECLARE @LoginName NVARCHAR(100) 
    DECLARE @HostName NVARCHAR(100)
    DECLARE @ServerName NVARCHAR(20) 
    DECLARE @LoginType NVARCHAR(20) 
    DECLARE @ClientHost NVARCHAR(20)
    DECLARE @DDLType VARCHAR(30)
    DECLARE @Grantor SYSNAME  
    DECLARE @Grantee SYSNAME
    DECLARE @Permission VARCHAR(30)
    DECLARE @ObjectName SYSNAME
    DECLARE @ObjectType VARCHAR(30)
    DECLARE @GrantOption BIT
    DECLARE @CascadeOption BIT
    DECLARE @TSQLCommand nVARCHAR(800)
     
    SET @Data = EVENTDATA()
    SET @HostName = HOST_NAME()
    SET @PostTime = @Data.value('(/EVENT_INSTANCE/PostTime)[1]', 'NVARCHAR(24)') 
    SET @LoginName = @Data.value('(/EVENT_INSTANCE/LoginName)[1]', 'NVARCHAR(100)')
    SET @ServerName = @Data.value('(/EVENT_INSTANCE/ServerName)[1]', 'NVARCHAR(100)')
    SET @ClientHost = @Data.value('(/EVENT_INSTANCE/ClientHost)[1]', 'NVARCHAR(100)')
    SET @LoginType = @Data.value('(/EVENT_INSTANCE/LoginType)[1]', 'nvarchar(100)')
    SET @DDLType = @Data.value('(/EVENT_INSTANCE/EventType)[1]', 'nvarchar(100)')
    SET @Grantor = @Data.value('(/EVENT_INSTANCE/Grantor)[1]', 'nvarchar(100)')
    SET @Grantee = @Data.value('(/EVENT_INSTANCE/Grantees/Grantee)[1]', 'nvarchar(100)')
    SET @Permission = @Data.value('(/EVENT_INSTANCE/Permissions/Permission)[1]', 'nvarchar(100)')
    SET @ObjectName = @Data.value('(/EVENT_INSTANCE/ObjectName)[1]', 'nvarchar(100)')
    SET @ObjectType = @Data.value('(/EVENT_INSTANCE/ObjectType)[1]', 'nvarchar(100)')
    SET @GrantOption = @Data.value('(/EVENT_INSTANCE/GrantOption)[1]', 'nvarchar(100)')
    SET @CascadeOption = @Data.value('(/EVENT_INSTANCE/CascadeOption)[1]', 'nvarchar(5)')
    SET @TSQLCommand = @Data.value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'nvarchar(800)')
    --IF @ClientHost <> '<local machine>'
    --BEGIN
      
        INSERT  INTO loginAudit_log ( Posttime, Servername, Hostname, Loginame, Logintype, ClientHost,
                                      DDLType, Grantor, Grantee, Permission, ObjectName, ObjectType,
                                      GrantOption, CascadeOption, TSQLCommand )
        VALUES  ( @posttime, @ServerName, @hostname, @loginname, @LoginType, @ClientHost, @DDLType, @Grantor,
                  @Grantee, @Permission, @ObjectName, @ObjectType, @GrantOption, @CascadeOption,
                  @TSQLCommand )
        EXEC msdb.dbo.sp_send_dbmail @profile_name = 'dbmail',
                  @recipients = '[email protected]',
                  @subject = N'登陸帳戶及權限變更',
                  @body = @TSQLCommand
      --END
END;

注意:在創建觸發器時,要修改郵件的配置文件名稱,及接收人。

測試登陸賬戶及權限變更審計觸發器

測試腳本

CREATE LOGIN [Jack] WITH PASSWORD=N'Password'
   , DEFAULT_DATABASE=[master]
   , DEFAULT_LANGUAGE=[簡體中文]
   , CHECK_EXPIRATION=OFF
   , CHECK_POLICY=OFF
GO
ALTER LOGIN [Jack] DISABLE
GO
ALTER LOGIN [Jack] ENABLE
GO
GRANT VIEW ANY DATABASE TO Jack WITH GRANT OPTION
REVOKE VIEW ANY DATABASE FROM Jack
DENY VIEW ANY DATABASE TO Jack CASCADE
EXEC sys.sp_addsrvrolemember Jack,sysadmin
EXEC sys.sp_dropsrvrolemember Jack,sysadmin
IF  EXISTS (SELECT * FROM sys.server_principals WHERE name = N'Jack')
DROP LOGIN [Jack]

測試結果

注意:在SQL Server 2008中,使用sys.sp_dropsrvrolemember刪除固定角色成員無法觸發。

EXEC sys.sp_dropsrvrolemember Jack,sysadmin

在SQL Server 2016中創建觸發器tr_LoginPermission_Audit時,啓用事件ALTER_SERVER_ROLE,CREATE_SERVER_ROLE,並進行如下測試:

--SQL Server 2016 測試創建服務器角色、賦予權限、添加成員、刪除成員、刪除用戶創建服務器角色
USE [master]
GO
CREATE SERVER ROLE [serverRoleName]
GO
GRANT VIEW ANY DATABASE TO serverRoleName
DENY CONTROL SERVER TO serverRoleName
EXEC sp_addsrvrolemember Jack_login, serverRoleName
--先刪除用戶創建的角色的所有成員,再刪除用戶創建的服務器角色
DECLARE @RoleName SYSNAME
SET @RoleName = N'serverRoleName'
IF @RoleName <> N'public' and (select is_fixed_role from sys.server_principals where name = @RoleName) = 0
BEGIN
    DECLARE @RoleMemberName sysname
    DECLARE Member_Cursor CURSOR FOR
    select [name]
    from sys.server_principals
    where principal_id in (
        select member_principal_id
        from sys.server_role_members
        where role_principal_id in (
            select principal_id
            FROM sys.server_principals where [name] = @RoleName  AND type = 'R' ))
    OPEN Member_Cursor;
    FETCH NEXT FROM Member_Cursor
    into @RoleMemberName
    DECLARE @SQL NVARCHAR(4000)
       
    WHILE @@FETCH_STATUS = 0
    BEGIN
       
        SET @SQL = 'ALTER SERVER ROLE '+ QUOTENAME(@RoleName,'[') +' DROP MEMBER '+ QUOTENAME(@RoleMemberName,'[')
        EXEC(@SQL)
       
        FETCH NEXT FROM Member_Cursor
        into @RoleMemberName
    END;
    CLOSE Member_Cursor;
    DEALLOCATE Member_Cursor;
END
DROP SERVER ROLE [serverRoleName]
GO

測試結果:

從測試結果可以看出,刪除用戶創建的服務器角色、刪除用戶創建的服務器角色的成員均不能觸發。

數據庫用戶(包含用戶)及權限變更的審計

同上面登陸賬戶情景一樣,下面給出了數據庫用戶及權限變更信息記錄日誌表:

USE master
GO
CREATE TABLE dbo.UserAudit_Log
    (
     Id INT IDENTITY(1, 1)
    ,Posttime DATETIME NULL
    ,Servername sysname NULL
    ,Hostname sysname NULL
    ,Loginame sysname NULL
    ,UserName sysname NULL
    ,ClientHost VARCHAR(20) NULL
    ,DBName sysname NULL
    ,SchemaName sysname NULL
    ,DDLType VARCHAR(30) NULL
    ,Grantor SYSNAME NULL
    ,Grantee SYSNAME NULL
    ,Permission VARCHAR(30) NULL
    ,AsGrantor sysname NULL
    ,ObjectName SYSNAME NULL
    ,ObjectType VARCHAR(30) NULL
    ,GrantOption BIT NULL
    ,CascadeOption BIT NULL
    ,TSQLCommand NVARCHAR(800) NULL
    )
    --DefaultSchema
    --RoleName

和服務器觸發器不同,接下來我們需要在各數據庫中創建數據庫及觸發器,如下給出了在數據庫test下創建的觸發器Tr_UserPermission_Audit:

USE test
go
CREATE TRIGGER Tr_UserPermission_Audit ON DATABASE
    FOR DDL_APPLICATION_ROLE_EVENTS,DDL_GDR_DATABASE_EVENTS
            ,DDL_ROLE_EVENTS,DDL_USER_EVENTS
AS
BEGIN 
    DECLARE @Data XML 
    DECLARE @PostTime NVARCHAR(24) 
    DECLARE @LoginName sysname  
    DECLARE @HostName sysname
    DECLARE @ServerName sysname
    DECLARE @ClientHost NVARCHAR(20) 
    DECLARE @UserName sysname
    DECLARE @DBName sysname
    DECLARE @SchemaName sysname
    DECLARE @DDLType VARCHAR(30)
    DECLARE @Grantor SYSNAME  
    DECLARE @Grantee SYSNAME
    DECLARE @AsGrantor sysname
    DECLARE @Permission VARCHAR(30)
    DECLARE @ObjectName SYSNAME
    DECLARE @ObjectType VARCHAR(30)
    DECLARE @GrantOption BIT
    DECLARE @CascadeOption BIT
    DECLARE @TSQLCommand nVARCHAR(800)
     
    SET @Data = EVENTDATA()
    SET @HostName = HOST_NAME()
    SET @PostTime = @Data.value('(/EVENT_INSTANCE/PostTime)[1]', 'NVARCHAR(24)') 
    SET @LoginName = @Data.value('(/EVENT_INSTANCE/LoginName)[1]', 'NVARCHAR(100)')
    SET @ServerName = @Data.value('(/EVENT_INSTANCE/ServerName)[1]', 'NVARCHAR(100)')
    SET @[email protected]('(/EVENT_INSTANCE/DatabaseName)[1]', 'NVARCHAR(100)')
    SET @[email protected]('(/EVENT_INSTANCE/SchemaName )[1]', 'NVARCHAR(100)')
    IF @SchemaName='' OR @SchemaName is NULL
            SET @[email protected]('(/EVENT_INSTANCE/DefaultSchema)[1]', 'NVARCHAR(100)')
    SET @[email protected]('(/EVENT_INSTANCE/UserName)[1]', 'NVARCHAR(100)')
    SET @ClientHost = @Data.value('(/EVENT_INSTANCE/ClientHost)[1]', 'NVARCHAR(100)')
    SET @DDLType = @Data.value('(/EVENT_INSTANCE/EventType)[1]', 'nvarchar(100)')
    SET @Grantor = @Data.value('(/EVENT_INSTANCE/Grantor)[1]', 'nvarchar(100)')
    SET @Grantee = @Data.value('(/EVENT_INSTANCE/Grantees/Grantee)[1]', 'nvarchar(100)')
    SET @[email protected]('(/EVENT_INSTANCE/AsGrantor)[1]', 'nvarchar(100)')
    SET @Permission = @Data.value('(/EVENT_INSTANCE/Permissions/Permission)[1]', 'nvarchar(100)')
    SET @ObjectName = @Data.value('(/EVENT_INSTANCE/ObjectName)[1]', 'nvarchar(100)')
    SET @ObjectType = @Data.value('(/EVENT_INSTANCE/ObjectType)[1]', 'nvarchar(100)')
    SET @GrantOption = @Data.value('(/EVENT_INSTANCE/GrantOption)[1]', 'nvarchar(100)')
    SET @CascadeOption = @Data.value('(/EVENT_INSTANCE/CascadeOption)[1]', 'nvarchar(5)')
    SET @TSQLCommand = @Data.value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'nvarchar(800)')
    --IF @ClientHost <> '<local machine>'
    --BEGIN
      
        INSERT  INTO MASTER.dbo.UserAudit_Log ( Posttime, Servername, Hostname, Loginame,  ClientHost,UserName,DBName,SchemaName,
                                      DDLType, Grantor, Grantee,AsGrantor, Permission, ObjectName, ObjectType,
                                      GrantOption, CascadeOption, TSQLCommand )
        VALUES  ( @posttime, @ServerName, @hostname, @loginname, @ClientHost,@UserName,@DBName,@SchemaName, @DDLType, @Grantor,
                  @Grantee,@AsGrantor, @Permission, @ObjectName, @ObjectType, @GrantOption, @CascadeOption,
                  @TSQLCommand )
        DECLARE @bodytxt nvarchar(MAX)
        SET @bodytxt=N'數據庫'+@DBName+N'變更TSQL:'+CHAR(10)+@TSQLCommand
        EXEC msdb.dbo.sp_send_dbmail @profile_name = 'dbmail',
                  @recipients = '[email protected]',
                  @subject = N'數據庫用戶\角色及權限變更',
                  @body = @bodytxt
      --END
END; 

注意:1. 在創建此觸發器時同樣需要修改郵件的配置文件名稱及接收人

          2. 在每個需要監控的數據庫下均要創建此觸發器

如下給出簡單的測試樣例

--SQL Server 2008測試樣例
USE test
GO
CREATE USER Jack FOR LOGIN Jack
CREATE USER Jack1 WITHOUT LOGIN
ALTER USER Jack1 WITH DEFAULT_SCHEMA=test
GRANT ALTER TO Jack WITH GRANT OPTION
REVOKE ALTER TO Jack CASCADE
DENY EXEC ON OBJECT::dbo.SimpleProc TO Jack
GRANT SELECT ON OBJECT::test(a,b) TO Jack
CREATE ROLE [testRoleIntest] AUTHORIZATION [dbo]
GO
DENY EXEC ON OBJECT::dbo.SimpleProc TO [testRoleIntest]
EXEC sys.sp_addrolemember [testRoleIntest],Jack
EXEC sys.sp_droprolemember [testRoleIntest], Jack
ALTER ROLE testRoleIntest WITH NAME=testRole;
CREATE APPLICATION ROLE [AppRole] WITH DEFAULT_SCHEMA = [dbo], PASSWORD =N'Pass,12word'
GRANT CONTROL TO [AppRole]
GO
GRANT CONTROL ON APPLICATION ROLE::[AppRole] TO [Jack] AS [AppRole]
GO
ALTER APPLICATION ROLE AppRole WITH PASSWORD=N'New!2Pwd'
DROP USER Jack
DROP USER Jack1
DROP ROLE [testRole]
DROP APPLICATION ROLE [AppRole]

SQL Server 2012 及以上測試環境測試樣例:

--SQL  Server 2012及以上環境
CREATE USER Jack WITH password='Password,@1'
ALTER USER Jack WITH ALLOW_ENCRYPTED_VALUE_MODIFICATIONS =  ON
DROP USER Jack
CREATE ROLE testRole
ALTER ROLE testRole ADD member Jack
ALTER ROLE testRole DROP member Jack

測試結果

從測試結果可以看到,創建完觸發器後,我們可以及時收到登陸賬戶、數據庫用戶及權限變更的預警,確保數據庫的安全。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章