如何撤销似乎“卡住”登录的 SA 权限?

yhe*_*hur 4 sql-server permissions role sa

如果我在这里没有正确使用一些术语,请提前道歉。我不是 DBA,但我是暂时被迫担任这个角色的。

我继承了一个在 SQL Server 2016 Enterprise 上运行的应用程序 - 一个实例用于 PROD,另一个实例用于 TEST。这些实例以“始终开启”的配置进行设置,因此我有一个用于 PROD 的可用性组和一个用于 TEST 的可用性组。每个可用性组中有两台服务器。每个实例都有多个数据库,但每个实例中只有一个数据库实际由应用程序使用 - 其他数据库看起来是为了测试目的而创建的。我只是提到还有其他数据库,以防万一它可能以某种我不知道的方式相关。

我有一个服务帐户的登录名(Windows 身份验证),并且该登录名映射到一些数据库中的同名用户。此服务帐户是 sysadmin 角色的成员。经过 IT 安全审核后,我被告知要删除此帐户的 SA 权限。我认为这不会给应用程序带来任何问题,但当然我想首先删除 TEST 实例中的权限,以便我可以确认应用程序由于某种原因不需要它们。

在测试实例中,我取消了登录的 sysadmin 角色,现在只选中了“公共”框,但我发现我仍然可以作为服务帐户登录并访问它之前有权访问的所有内容。它仍然可以访问主数据库中的所有表(这是我所期望的,因为该帐户已被授予对数据库中 dbo 架构的很多权限),但令我困惑的是它仍然可以查看服务器级别,我认为它会失去。

如果我在以服务帐户身份登录时运行此语句 ( SELECT * FROM fn_my_permissions(NULL, 'server');),则这是我获得的权限列表:

  • 连接SQL
  • 关闭
  • 创建端点
  • 创建任何数据库
  • 创建可用性组
  • 更改任何登录信息
  • 更改任何凭证
  • 更改任何端点
  • 更改任何链接的服务器
  • 改变任何连接
  • 更改任何数据库
  • 改变资源
  • 更改设置
  • 改变轨迹
  • 更改任何可用性组
  • 管理批量操作
  • 验证服务器
  • 外部检修组件
  • 查看任何数据库
  • 查看任何定义
  • 查看服务器状态
  • 创建 DDL 事件通知
  • 创建跟踪事件通知
  • 更改任何事件通知
  • 改变服务器状态
  • 不安全的装配
  • 更改任何服务器审核
  • 创建服务器角色
  • 更改任何服务器角色
  • 更改任何活动会话
  • 连接任何数据库
  • 冒充任何登录
  • 选择所有用户安全
  • 控制服务器

这些对我来说似乎是 SA 级别的权限,但我不明白它们来自哪里,因为 sysadmin 角色不再应用于登录。我认为它们可能已被单独授予登录权限(这对我来说似乎很奇怪,因为这不是绕过了首先拥有角色的目的吗?)所以我尝试撤销它们。撤销命令似乎成功(“命令成功完成”),但是当我运行相同的命令再次检查服务帐户的权限时,它仍然具有所有相同的权限。

我还发现,当以服务帐户身份登录时,它可以将 sysadmin 角色分配给自身(并将其从自身中删除)。

其他注意事项:每次对帐户进行更改时,我都会小心确保在属于可用性组的两台服务器上进行相同的更改。我的理解是,即使它们映射到同一数据库级用户,每个服务器上的登录都是单独的,因此对登录的任何更改都必须在两台服务器上完成。

我尝试过的另一件事是从 PROD 实例的辅助节点上的帐户中删除 sysadmin 角色,只是为了看看我是否有同样的问题,即权限“卡住”,并且问题不会在那里发生。

我觉得我在这里遗漏了一些明显的东西,但我不知道它是什么。任何帮助将非常感激。

AMt*_*two 5

用户在 SQL Server 中获取权限有几种不同的方式。除了角色成员身份(如sysadmin固定服务器角色)之外,您还可以GRANT对单独的权限执行显式操作。由于您使用的是 Windows 服务帐户,因此除了直接授予登录名的任何权限之外,还可以将权限授予域组,并继承这些权限。

该登录名可能是域组的一部分。

在 Active Directory/域方面,您的服务帐户可能包含在 AD 组中,然后sysadmin在 SQL Server 实例中授予该 AD 组。然后,即使没有直接授予登录名sysadmin,它仍然会继承该组的成员身份。

您可以通过系统存储过程从 SQL Server 端检查哪些组正在授予哪些成员资格xp_logininfo

EXEC xp_logininfo @acctname = 'MyDomain\amtwo', @option = 'ALL';
Run Code Online (Sandbox Code Playgroud)

输出看起来像这样。请注意第一行,它显示我的amtwo登录admin通过SQLSERVER_SYSADMIN域组中的“权限路径”(读取:组成员身份)获得权限。

account name     type     privilege mapped login name   permission path
---------------- -------- --------- ------------------- ----------------------------
MyDomain\amtwo   user     admin     MyDomain\amtwo      MyDomain\SQLSERVER_SYSADMIN
MyDomain\amtwo   user     user      MyDomain\amtwo      MyDomain\SomeOtherGroup
Run Code Online (Sandbox Code Playgroud)

在这种情况下,如果我想sysadmin从登录中删除权限amtwo,我需要将其从SQLSERVER_SYSADMIN活动目录端的组中删除。(即,我无法通过 SQL Server 配置和权限对此执行任何操作。)

你可能只是有CONTROL SERVER

有一个名为 的权限几乎CONTROL SERVER与相同,但有一些区别。将服从强制执行更细粒度的权限(覆盖所有内容,包括权限)。此外,每隔一段时间,您可能会遇到适用于-- 的旧命令或函数,但这种情况越来越罕见。sysadminCONTROL SERVERDENYsysadminDENYsysadmin

可能无论是谁设置了您的服务帐户, sysadmin授予了成员身份和CONTROL SERVER权限。

检查这一点的最佳方法是使用脚本来查询所有权限。该查询来自我的开源 DBA 数据库中的视图,但也在此包含完整的查询。

如前所述,这将为所有用户授予所有服务器级别的权限。您只需在 where 子句中添加一行即可进一步过滤。由于这是 Windows 登录,因此您应该检查直接授予该登录的权限以及该登录所属的任何组(这将是使用xp_logininfo.

-- From http://am2.co/dbadb - Licensed under BSD 2-clause
    SELECT 
            LoginSid                = p.sid, 
            LoginName               = p.name, 
            LoginType               = p.type_desc,
            DefaultDatabase         = p.default_database_name,
            LoginIsEnabled          = IIF(p.is_disabled = 0,1,0),
            CanLogIn                = COALESCE((SELECT TOP 1 1 FROM sys.server_permissions AS cosq
                                                WHERE cosq.grantee_principal_id = p.principal_id
                                                AND cosq.type = 'COSQ' 
                                                AND cosq.state IN ('G','W')
                                                AND p.is_disabled = 0
                                                ),
                                        0),
            PermissionType          = perm.type,
            PermissionState         = perm.state,
            PermissionSql           = CONCAT(perm.state_desc, N' ',
                                                perm.permission_name, N' TO ',
                                                QUOTENAME(p.name) COLLATE Latin1_General_CI_AS_KS_WS, 
                                                N';'
                                                ),
            DateLoginCreated        = p.create_date,
            DateLoginModified       = p.modify_date
    FROM sys.server_principals AS p
    JOIN sys.server_permissions AS perm 
        ON perm.grantee_principal_id = p.principal_id
    WHERE p.type IN ('S','U','G')
    AND p.name <> N'sa'
    AND p.name NOT LIKE N'##%##';
Run Code Online (Sandbox Code Playgroud)

如果您看到任何权限(例如CONTROL SERVER您想要删除的权限),您可以使用类似 的语法来执行此操作REVOKE CONTROL SERVER FROM 'MyDomain\Login';。请注意,该PermissionSql列将返回一个GRANTorDENY语句,您可以简单地修改该 sql 文本以将其更改GRANTREVOKE.