SQL Server登陆账户、数据库用户(包含用户)及权限的自动化审计(续)

不负韶华,只争朝夕!在抗击新冠疫情的日子里,从每天关注疫情动态的恐慌,在家中的彷徨,到坚定自己的信念。也许上天认为我们走的太匆忙,希望我们静下来,重新规划一下未来;希望可以进行沉淀,以让我们走的更高、更远。在最近的日子里,在完成线上工作之余,我开始总结,并把一些经验记录下来,分享出来,和大家共勉。

闲话不多说,接下来还是直接上正文。

《SQL Server登陆账户、数据库用户(包含用户)及权限的自动化审计》一文中我分享了对登陆账户、数据库用户及权限变更的即时预警,除此之外,我们还需要定期全面检查登陆账户、数据库用户及权限,收回不必要的登陆账户、数据库用户及权限,以减少数据库安全风险。要对历史的登录账户/数据库用户及权限进行比较,首先需要记录历史登录账户/数据库用户及权限信息。下面创建两个表分别记录登录账户及权限信息,数据库用户及权限信息:

--创建登陆帐户权限信息表
USE master
GO
CREATE TABLE DBA_LoginsPermissions_Info
    (class_desc NVARCHAR(60)
    ,permission_name NVARCHAR(128)
    ,GDR nvarchar(60)
    ,granteeName sysname NULL
    ,objectName NVARCHAR(60)
    ,TSQLTxt NVARCHAR(800)
    ,InsertDate DATETIME DEFAULT(GETDATE()))
--创建数据库用户权限信息表
CREATE TABLE DBA_UsersPermissions_Info
    (dbName sysname NULL
    ,class_desc NVARCHAR(60)
    ,permission_name NVARCHAR(128)
    ,GDR nvarchar(60)
    ,granteeName sysname NULL
    ,objectName NVARCHAR(60)
    ,TSQLTxt NVARCHAR(800)
    ,InsertDate DATETIME DEFAULT(GETDATE()))

注意,如果留意的话,可能已经发现表DBA_UsersPermissions_Info 在前文已经创建,如果你已经创建它,本文中的创建该表的​脚本可以忽略。

下面我们来创建获取登陆账户、数据库用户及权限信息的过程:

USE master
GO
/*记录登陆帐户、数据库用户及权限信息*/
CREATE PROC DBA_GetLoginsUsersPermissions_Info AS     
BEGIN
      SET NOCOUNT ON
      --记录当前登陆帐户权限信息
      DECLARE @LoginName sysname
      DECLARE @sid VARBINARY(85)
      DECLARE cur CURSOR FOR
      SELECT name,sid FROM sys.sql_logins
      WHERE is_disabled=0
      OPEN cur
      FETCH NEXT FROM cur INTO @LoginName,@sid
      WHILE @@FETCH_STATUS=0
      BEGIN
            INSERT INTO DBA_LoginsPermissions_Info(class_desc,permission_name,GDR,granteeName,objectName,TSQLTxt)
            EXEC sp_DBA_LoginPermissionsClone @LoginName=@LoginName,@NewLoginName=@LoginName,@print=0
            --记录当前数据库用户权限信息
            DECLARE @DBName sysname
            DECLARE @UserName sysname
            DECLARE @tsql NVARCHAR(200)
            DECLARE db CURSOR FOR
            SELECT name FROM sys.databases
            OPEN db
            FETCH NEXT FROM db INTO @DBName
            WHILE @@FETCH_STATUS=0
            BEGIN
                  SET @tsql=N'SELECT @UserName=name from '+QUOTENAME(@dbName,']')+N'.sys.sysusers WHERE sid=@sid'
                  EXEC sys.sp_executesql @tsql,N'@UserName sysname output,@sid VARBINARY(85)',@UserName OUTPUT,@sid
                  --INSERT INTO master.dbo.DBA_UsersPermissions_Info(dbName,class_desc,permission_name,GDR,granteeName,objectName,TSQLTxt)
                  exec sp_DBA_userPermisionsClone @dbName=@DBName,@userName=@UserName,@print=0
                  FETCH NEXT FROM db INTO @DBName
            END
            CLOSE db
            DEALLOCATE db
            FETCH NEXT FROM cur INTO @LoginName,@sid
      END
      CLOSE cur
      DEALLOCATE cur
      --增加包含数据库中包含用户权限信息获取及监控
      DECLARE @versionNum AS INT    --SQL Server 的版本号
      SET @versionNum = SUBSTRING(@@VERSION, 22, 4)
      IF @versionNum>=2012
      BEGIN
            DECLARE @db TABLE(NAME sysname)
            INSERT INTO @db ( NAME )
            EXEC ('SELECT  name
                  FROM    sys.databases
                  WHERE   containment = 1')
            DECLARE cur CURSOR FOR
            SELECT  name FROM @db
            OPEN cur
            FETCH NEXT FROM cur INTO @DBName
            WHILE @@FETCH_STATUS=0
            BEGIN
                  DECLARE @sql VARCHAR(200)
                  DECLARE @t TABLE (NAME sysname)
                  SET @sql='SELECT name from '+QUOTENAME(@DBName,']')+'.sys.database_principals WHERE authentication_type=2 '
                  INSERT INTO @t ( NAME )
                  EXEC(@sql)
                  DECLARE users CURSOR FOR
                  SELECT name from [@t]
                  OPEN users
                  FETCH NEXT FROM users INTO @UserName
                  WHILE @@FETCH_STATUS=0
                  BEGIN
                        exec sp_DBA_userPermisionsClone @dbName=@DBName,@userName=@UserName,@print=0
                        FETCH NEXT FROM users INTO @UserName
                  END
                  CLOSE users
                  DEALLOCATE users
                  FETCH NEXT FROM cur INTO @DBName
            END
      END
END
GO

注意,在创建这个过程之前需要先创建 sp_DBA_LoginPermissionsClone 及 sp_DBA_userPermisionsClone 。

下面我们直接执行过程,获取当前的登陆账户、数据库用户及权限信息,脚本如下:

--记录当前登陆帐户、数据库用户及权限信息
EXEC DBA_GetLoginsUsersPermissions_Info
GO

检查登陆账户及权限信息​:

SELECT * FROM DBA_LoginsPermissions_Info

检查用户权限及信息​:

SELECT * FROM DBA_UsersPermissions_Info

接下来我们创建审计登陆账户、数据库用户权限的过程:

/*创建定期检查过程*/
CREATE PROC DBA_LoginsUsersPermissions_Audit AS
BEGIN
      SET NOCOUNT ON
      --记录当前登陆帐户、数据库用户及权限信息
      EXEC DBA_GetLoginsUsersPermissions_Info
      DECLARE @bodyHTML nvarchar(MAX)
      --删除已经回收的登陆帐户、权限信息
      DELETE master.dbo.DBA_LoginsPermissions_Info
      WHERE TSQLTxt NOT IN( SELECT TSQLTxt FROM master.dbo.DBA_LoginsPermissions_Info WHERE insertdate>=CAST(GETDATE() AS date))
            AND insertdate<CAST(GETDATE() AS date)
      --保存新增登陆帐户、权限信息
      DELETE master.dbo.DBA_LoginsPermissions_Info
      WHERE TSQLTxt IN( SELECT TSQLTxt FROM master.dbo.DBA_LoginsPermissions_Info WHERE insertdate<CAST(GETDATE() AS date))
            AND insertdate>CAST(GETDATE() AS date)
            
      --删除已经回收的数据库用户、权限信息
      DELETE master.dbo.DBA_UsersPermissions_Info
      WHERE TSQLTxt NOT IN( SELECT TSQLTxt FROM master.dbo.DBA_UsersPermissions_Info WHERE insertdate>=CAST(GETDATE() AS date))
            AND insertdate<CAST(GETDATE() AS date)
      --保存新增数据库用户、权限信息
      DELETE master.dbo.DBA_UsersPermissions_Info
      WHERE TSQLTxt IN( SELECT TSQLTxt FROM master.dbo.DBA_UsersPermissions_Info WHERE insertdate<CAST(GETDATE() AS date))
            AND insertdate>CAST(GETDATE() AS date)
      IF EXISTS(SELECT TSQLTxt FROM master.dbo.DBA_LoginsPermissions_Info WHERE insertdate>=CAST(GETDATE() AS date))
      BEGIN
            SET @bodyHTML = N'<style>td{FONT-FAMILY: 宋体, Helvetica, sans-serif; FONT-SIZE: 12px;
            LINE-HEIGHT: 20px; TEXT-DECORATION: none;BORDER-BOTTOM: black 1px solid;
            BORDER-RIGHT: black 1px solid;}th{FONT-FAMILY: 宋体, Helvetica, sans-serif; FONT-SIZE: 12px;
            LINE-HEIGHT: 20px; TEXT-DECORATION: none;BORDER-BOTTOM: black 1px solid;BORDER-RIGHT: black 1px solid;}</style>' 
            + N'<H1>[登陆帐户及权限变更]</H1>' 
            + N'<table border="0" cellspacing="0" cellpadding="0" style="BORDER-BOTTOM: black 1px solid;
                  BORDER-LEFT: black 1px solid; BORDER-RIGHT: black 1px solid;BORDER-TOP: black 1px solid">' 
            + N'<tr><th>对象类型</th><th>权限</th>'
            + N'<th>GRANT OR DENY</th>'
            + N'<th>授权者</th>'
            + N'<th>授权对象</th><th>执行脚本</th></tr>' 
            + CAST(( 
             SELECT td = ISNULL(class_desc,'') ,  '' , 
                        td = ISNULL(permission_name ,''),  '' , 
                        td = ISNULL(GDR,''),  '' , 
                        td = ISNULL(granteeName ,''),  '' , 
                        td = ISNULL(objectName,'') ,  '' , 
                        td = ISNULL(TSQLTxt,'')
            FROM  master.dbo.DBA_LoginsPermissions_Info h
            WHERE insertdate>=CAST(GETDATE() AS date) 
                     FOR 
                         XML PATH('tr') , 
                               TYPE 
                     ) AS NVARCHAR(MAX)) + N'</table>'; 
      END
      IF EXISTS (SELECT TSQLTxt FROM master.dbo.DBA_UsersPermissions_Info WHERE insertdate>=CAST(GETDATE() AS date))
      BEGIN
            SET @bodyHTML =@bodyHTML+ N'<style>td{FONT-FAMILY: 宋体, Helvetica, sans-serif; FONT-SIZE: 12px;
            LINE-HEIGHT: 20px; TEXT-DECORATION: none;BORDER-BOTTOM: black 1px solid;
            BORDER-RIGHT: black 1px solid;}th{FONT-FAMILY: 宋体, Helvetica, sans-serif; FONT-SIZE: 12px;
            LINE-HEIGHT: 20px; TEXT-DECORATION: none;BORDER-BOTTOM: black 1px solid;BORDER-RIGHT: black 1px solid;}</style>' 
            + N'<H1>[数据库用户及权限变更]</H1>' 
            + N'<table border="0" cellspacing="0" cellpadding="0" style="BORDER-BOTTOM: black 1px solid;
                  BORDER-LEFT: black 1px solid; BORDER-RIGHT: black 1px solid;BORDER-TOP: black 1px solid">' 
            + N'<tr><th>数据库</th><th>对象类型</th><th>权限</th>'
            + N'<th>GRANT OR DENY</th>'
            + N'<th>授权者</th>'
            + N'<th>授权对象</th><th>执行脚本</th></tr>' 
            + CAST(( 
             SELECT td = ISNULL(dbName,'') ,  '' , 
                        td = ISNULL(class_desc,'') ,  '' , 
                        td = ISNULL(permission_name,'') ,  '' , 
                        td = ISNULL(GDR,''),  '' , 
                        td = ISNULL(granteeName,'') ,  '' , 
                        td = ISNULL(objectName,'') ,  '' , 
                        td = ISNULL(TSQLTxt,'')
            FROM  master.dbo.DBA_UsersPermissions_Info h
            WHERE insertdate>=CAST(GETDATE() AS date)
                     FOR 
                         XML PATH('tr') , 
                               TYPE 
                     ) AS NVARCHAR(MAX)) + N'</table>'; 
      END
      --发送邮件预警
      DECLARE @profile_name sysname
      SELECT @profile_name=name
      from msdb.dbo.sysmail_profile
      EXEC msdb.dbo.sp_send_dbmail @profile_name = @profile_name,
                    @recipients = 'Jack@dba.com',
                    @subject = N'登陆帐户及权限变更',
                    @body = @bodyHTML,
                    @body_format = 'html';
END

注意:在创建DBA_LoginsUsersPermissions_Audit 时需要将邮件接收人修改为相关责任人。

下面创建周期检验作业​:​

USE [msdb]
GO
EXEC msdb.dbo.sp_add_job
      @job_name = N'DBA_登陆账户数据库用户权限周期性审计'
      , @owner_login_name = N'Jack'
USE [msdb]
GO
EXEC msdb.dbo.sp_add_jobstep
      @job_name = N'DBA_登陆账户数据库用户权限周期性审计'
      , @step_name = N'登陆账户数据库用户权限周期性审计'
      , @step_id = 1
    , @subsystem = N'TSQL'
    , @command = N'EXEC DBA_LoginsUsersPermissions_Audit'
    , @database_name = N'master'
GO
USE [msdb]
GO
EXEC msdb.dbo.sp_add_jobschedule
      @job_name = N'DBA_登陆账户数据库用户权限周期性审计'
      , @name = N'登陆账户数据库用户权限周期性审计周期'
      , @enabled = 1
    , @freq_type = 8
    , @freq_interval = 2
    , @freq_subday_type = 1
    , @freq_subday_interval = 0
    , @freq_relative_interval = 0
    , @freq_recurrence_factor = 1
    , @active_start_date = 20200206
    , @active_end_date = 99991231
    , @active_start_time =80000
    , @active_end_time = 235959

这里的作业执行周期为一周执行一次(每周一早上8点执行)。

最后我们来做两个简单测试,以检验作业的有效性​:

--测试样例
USE master
GO
GRANT VIEW ANY DATABASE TO Jack
​
USE test
GRANT SELECT TO Jack

测试结果:

 

至此,我们既可以即时预警登陆账户、数据库用户及权限变更,也可以周期全面的检查登陆账户、数据库用户及权限。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值