AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 270457
Accepted
Brent Ozar
Brent Ozar
Asked: 2020-07-07 06:31:59 +0800 CST2020-07-07 06:31:59 +0800 CST 2020-07-07 06:31:59 +0800 CST

如何拒绝写入所有 SQL Server 数据库(包括新的和恢复的)

  • 772

我想创建一个被拒绝写入任何数据库的登录名 - 不仅仅是今天存在的数据库,还有任何新创建的数据库或从其他服务器恢复的数据库。

我不能使用仅数据库角色(甚至在模型中),因为这些角色不会对新恢复的数据库生效。

(商业目的:我正在写一篇关于人们如何为自己设置低权限的新登录的博客文章,并且他们可以确保他们在复制/粘贴代码时不会意外地出现“糟糕”的时刻或在没有 where 子句的情况下运行它。)

sql-server security
  • 7 7 个回答
  • 1922 Views

7 个回答

  • Voted
  1. Best Answer
    Rob Dalzell
    2020-07-07T13:56:29+08:002020-07-07T13:56:29+08:00

    如果您可以在执行之间有有限的时间(最少 10 秒)来捕获新的/恢复的数据库,您可以创建一个计划的 SQL Server 代理作业并将类似这样的内容放入 T-SQL 作业步骤:-

    SET NOCOUNT ON
    
    DECLARE @command nvarchar(max) ;
    
    SET @command = 
    N'SET NOCOUNT ON
    
    DECLARE @UserName sysname ;
    
    SELECT @UserName = USRS.[name] 
    FROM sys.database_principals AS USRS
    INNER JOIN sys.server_principals AS LGNS ON LGNS.[sid] = USRS.[sid]
    WHERE LGNS.[name] = ''LoginName'' ;
    
    IF IS_ROLEMEMBER(''db_denydatawriter'', @UserName) = 0
       BEGIN
          EXEC sp_addrolemember ''db_denydatawriter'', @UserName ;
       END ;' ;
    
    EXEC sp_ineachdb 
       @command      = @command, 
       @state_desc   = N'ONLINE', 
       @is_read_only = 0 ;
    
    • 15
  2. Stephen Morris - Mo64
    2020-07-07T06:50:08+08:002020-07-07T06:50:08+08:00

    你不能在 IUD 上使用登录触发器和拒绝吗?每次用户登录时,我想它都需要遍历数据库并将 DENY 添加到任何新添加的数据库中。

    • 0
  3. logitestus
    2020-07-07T12:46:57+08:002020-07-07T12:46:57+08:00

    有人可以使这更简单,但这是我的方法:

    1. 为“只读管理员”创建服务器角色并分配以下权限以允许特定功能的服务器范围权限,例如查看所有对象的定义和查看服务器状态的能力。这也为您要分配给各个数据库角色的用户创建了一个“桶”:

      USE [master]
      GO
      CREATE SERVER ROLE [ReadOnlyAdmins]
      GO
      GRANT CONNECT SQL TO [ReadOnlyAdmins]
      GO
      GRANT VIEW ANY DATABASE TO [ReadOnlyAdmins]
      GO
      GRANT VIEW ANY DEFINITION TO [ReadOnlyAdmins]
      GO
      GRANT VIEW SERVER STATE TO [ReadOnlyAdmins]
      GO
      
    2. 在 [master] 上创建存储过程以创建/更新特定于数据库的用户组,该用户组授予对指定数据库的读取但拒绝写入。我正在使用存储过程,因为您很可能需要在指定数据库的上下文中运行,因此动态 sql 是我想到的第一个解决方案。请注意,此解决方案强制您使用特定的组名,您可以将其更改为您喜欢的任何名称。

    编辑:如另一个解决方案中所述,存储过程需要检查正在添加/更新的数据库是否处于联机模式。如果不是,它应该每 5 秒左右循环并检查一次吗?

        CREATE PROCEDURE dbo.CreateDBReadOnlyAdmins
        (
             @dbName NVARCHAR(128)
        )
        AS
        BEGIN
        SET NOCOUNT ON;
        DECLARE @dbState NVARCHAR(500)
        select @dbState = state_desc from sys.databases where name = @dbName
        WHILE(@dbState <> 'ONLINE')
           BEGIN
              WAITFOR DELAY '00:00:05'
              select @dbState = state_desc from sys.databases where name = @dbName 
           END 
        DECLARE @sql NVARCHAR(MAX), @max_id int, @id int, @retval int, @ParmDefinition                 nvarchar(500);
        --Check to see if the Database Role has been created or not.  Add it if not.
        SET @sql = 'SET NOCOUNT ON;SELECT 1 FROM ' + @dbName + '.sys.database_principals                 WHERE type_desc = ''DATABASE_ROLE'' AND name = ''ReadOnlyAdmins'';'
        SET @ParmDefinition = N'@retvalOUT int OUTPUT';
        EXEC sp_executesql @SQL, @ParmDefinition, @retvalOUT=@retval OUTPUT;
        SELECT @id = ISNULL(@retval,0);
        IF (@id = 0)
            BEGIN 
                SET @sql = 'USE [' + @dbName + '];' +
                'CREATE ROLE [ReadOnlyAdmins];' +
                'GRANT SELECT ON ALL TO [ReadOnlyAdmins];' +
                'DENY INSERT, UPDATE, DELETE ON ALL TO [ReadOnlyAdmins];';
                EXEC @sql
                --SELECT (@sql)
            END
        
        
        IF(OBJECT_ID('tempdb..#role_members') IS NOT NULL) BEGIN DROP TABLE #role_members END;
        --Add all members of the ReadOnlyAdmins Server Role to the db-specific role.
        SELECT ROW_NUMBER() OVER (ORDER BY member.name) as rowid
            ,'ALTER ROLE [ReadOnlyAdmins] ADD MEMBER [' + member.name + '];' AS MemberName
        into #role_members
        FROM sys.server_role_members  
        JOIN sys.server_principals AS role  
            ON sys.server_role_members.role_principal_id = role.principal_id  
        JOIN sys.server_principals AS member  
            ON sys.server_role_members.member_principal_id = member.principal_id
        WHERE role.name = 'ReadOnlyAdmins';
        
        SET @sql = 'USE [' + @dbName + '];'
        
        select @max_id = MAX(rowid) from #role_members
        SET @id = 1
        
        WHILE @id <= @max_id
            BEGIN
                SELECT @sql = @sql + MemberName from #role_members where rowid = @id
                EXEC @sql
                --SELECT @sql
                SET @id = @id + 1
            END
        END
        GO
    
    1. 接下来是添加一个 ddl 触发器来覆盖所有 CREATE_DATABASE 和 ALTER_DATABASE 事件。此触发器需要进行微调以处理高可用性场景以及对兼容性、恢复模型等的随机更改。触发器还将解析出数据库名称。我从 [Bob Pusateri] https://www.bobpusateri.com/archive/2018/09/a-tale-of-a-trigger/ 窃取 HA 处理。此外,我不确定如何防止触发器触发非侵入性更新(例如兼容性、恢复模式、用户模式等)。我想一个更聪明的人需要处理这个问题。:)

      CREATE TRIGGER AddReadOnlyAdminsToDBs
      ON ALL SERVER
      FOR CREATE_DATABASE, ALTER_DATABASE
      AS
      BEGIN
          DECLARE @xml XML = EVENTDATA();
          DECLARE @var_xml NVARCHAR(128);
          DECLARE @error_msg VARCHAR(1024);
      
          SET @var_xml.value('(EVENT_INSTANCE/DatabaseName)[1]', 'VARCHAR(128)') 
      
          --uncomment the below line if you are running HADR
          --If sys.fn_hadr_is_primary_replica(@var_xml) = 1   
          BEGIN  
              EXEC master.dbo.CreateDBReadOnlyAdmins @var_xml 
          END  
      END
      GO
      
    2. 现在,无论何时在实例上添加新数据库或更新数据库,上述内容都涵盖了。当您只是在上面的 SERVER ROLE 中添加新登录时,您会怎么做?我的想法是创建另一个存储过程(或仅更改上述存储过程以处理所有本地数据库,然后将 SERVER ROLE 中的所有用户添加到所有非系统数据库)。然后将其与另一个仅查看 ADD_SERVER_ROLE_MEMBER 事件然后仅查看特定 SERVER ROLE“ReadOnlyAdmins”的 DDL 触发器结合使用。

    我发现的其他漏洞:

    • 如何处理 SQL 代理。我的意思是您可以为 MSDB 自定义创建一个特定于数据库的角色,以涵盖检查作业状态、查看作业日志等内容。
    • 如何处理复制。我不确定如何处理这种特殊情况。我需要更多地研究它。
    • 一些基于策略的管理能否更好地满足这一点?再一次,我需要做更多的研究。

    谢谢。

    • 0
  4. René Larsen
    2020-07-07T13:05:44+08:002020-07-07T13:05:44+08:00

    我发现这也许可以帮助恢复部分。

    在任何 RESTORE DATABASE 事件后自动执行存储过程

    DECLARE @fn VARCHAR(MAX); 
    SELECT @fn = SUBSTRING([path], 0, LEN([path])-CHARINDEX(CHAR(92), REVERSE([path]))+1) + CHAR(92) + 'Log.trc' 
    FROM sys.traces WHERE is_default = 1; 
    
    SELECT DatabaseName, StartTime, TextData 
    FROM sys.fn_trace_gettable(@fn, DEFAULT) 
    WHERE EventClass = 115 
     AND TextData LIKE '%RESTORE%'; -- since can't differentiate between backup/restore
    
    • 0
  5. Rusty
    2020-07-07T07:06:26+08:002020-07-07T07:06:26+08:00

    我认为会使用服务器范围的触发器,并且任何创建数据库或还原数据库命令将立即跟随创建用户并在新创建或还原的数据库上拒绝 IUD:

    https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql?view=sql-server-ver15

    CREATE TRIGGER ddl_trig_database
    ON ALL SERVER
    FOR CREATE_DATABASE
    AS

    去

    • -1
  6. Conamin
    2020-07-07T07:11:31+08:002020-07-07T07:11:31+08:00

    我在工作中提出了一个用例,其范围似乎相似,我被告知我必须允许某个帐户对新的和现有的数据库进行读取访问。我不想让用户能够在数据库和服务器级别进行更改。

    在研究了如何去做之后,我发现从 SQL Server 2016 开始,您可以创建一个自定义服务器角色,然后您可以应用两个授权。

    连接任何数据库并选择所有用户安全

    在这两个授权之间,您授予对当前存在、新创建或已从给定实例上的备份中恢复的所有数据库的只读访问权限。拒绝写入是隐含的,因为您必须授予服务器角色允许写入的权限。

    • -1
  7. Viorel Ciucu
    2020-07-07T12:29:41+08:002020-07-07T12:29:41+08:00

    从我的脑海中,也许创建一个登录名并拒绝它的所有内容,只允许通过自定义存储过程进行选择/插入/更新/删除?

    • -1

相关问题

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 存储过程可以防止 SQL 注入吗?

  • 死锁的主要原因是什么,可以预防吗?

  • 保护数据库密码

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve