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 / 问题 / 318375
Accepted
Muflix
Muflix
Asked: 2022-10-19 13:33:41 +0800 CST2022-10-19 13:33:41 +0800 CST 2022-10-19 13:33:41 +0800 CST

具有可信任的跨数据库访问不适用于服务帐户,也不适用于证书

  • 772

我有这个设置

在此处输入图像描述

  • 我创建了两个数据库(源和目标)。
  • 源数据库具有正在访问目标数据库中的表的存储过程。
  • 存储过程作为服务帐户执行
  • 服务帐户登录在两个数据库中都有用户帐户
  • 服务帐户具有目标数据库的连接和身份验证权限
  • 服务帐户对目标数据库架构具有读取权限
  • 两个数据库都值得信赖

我仍然有一个错误

服务器主体“ServiceAccount”无法在当前安全上下文下访问数据库“TargetDatabase”。

SQL 脚本

-- Create logins
CREATE LOGIN SourceDatabaseOwner    WITH PASSWORD = 'Pa$$w0rd'  
CREATE LOGIN TargetDatabaseOwner    WITH PASSWORD = 'Pa$$w0rd'  
CREATE LOGIN ServiceAccount         WITH PASSWORD = 'Pa$$w0rd'  

-- Create databases
CREATE DATABASE SourceDatabase
CREATE DATABASE TargetDatabase

-- Setup trustworthy
ALTER DATABASE SourceDatabase SET TRUSTWORTHY ON;
ALTER DATABASE TargetDatabase SET TRUSTWORTHY ON;

-- Setup database owners
USE SourceDatabase
GO
EXEC dbo.sp_changedbowner @loginame = N'SourceDatabaseOwner'

USE TargetDatabase
GO
EXEC dbo.sp_changedbowner @loginame = N'TargetDatabaseOwner'

-- Add ServiceAccount to source database
USE SourceDatabase
GO
CREATE USER ServiceAccount FOR LOGIN ServiceAccount;  

-- Add ServiceAccount to target database
USE TargetDatabase
GO
CREATE USER ServiceAccount FOR LOGIN ServiceAccount;  

-- Enable ServiceAccount to authenticate to target database
USE TargetDatabase
GO
GRANT AUTHENTICATE TO ServiceAccount;

-- Grant permissions
USE TargetDatabase
GO
GRANT SELECT ON SCHEMA::dbo TO ServiceAccount

-- Create table with data
USE TargetDatabase
GO
create table dbo.InterestingData (Id int identity primary key, Content nvarchar(255))
insert into dbo.InterestingData (Content) values ('Foo'), ('Bar')

-- Create stored procedure executing under service account and accessing target database
USE SourceDatabase
GO
create or alter procedure dbo.GetData
with execute as 'ServiceAccount' as
begin
    select id, content from TargetDatabase.dbo.InterestingData
end

-- Execution does not work under service account
-- The server principal "ServiceAccount" is not able to access the database "TargetDatabase" under the current security context.
exec dbo.GetData

所以我继续并尝试使用证书签署程序并在目标表上设置权限以登录该证书

-- create certificate in master
use master 
go

CREATE CERTIFICATE SignProcedureCert
ENCRYPTION BY PASSWORD = 'Pa$$w0rd'
WITH SUBJECT = 'Certificate for signing stored procedures'
GO

-- backup certificate
BACKUP CERTIFICATE SignProcedureCert TO FILE ='C:\Certs\SignProcedureCert.cer'
WITH PRIVATE KEY
(       
    FILE = 'C:\Certs\SignProcedureCert.pvk',
    DECRYPTION BY PASSWORD = 'Pa$$w0rd',
    ENCRYPTION BY PASSWORD = 'Pa$$w0rd'
)
GO

-- create login from the certificate
create login SignProcedureLogin from certificate SignProcedureCert

-- Import certificate to the source database
CREATE CERTIFICATE SignProcedureCert
FROM FILE = 'C:\Certs\SignProcedureCert.cer'
WITH PRIVATE KEY (FILE = 'C:\Certs\SignProcedureCert.pvk', 
ENCRYPTION BY PASSWORD = 'Pa$$w0rd', 
DECRYPTION BY PASSWORD = 'Pa$$w0rd')

-- Sign the procedure
ADD SIGNATURE TO dbo.GetData   
   BY CERTIFICATE SignProcedureCert  
    WITH PASSWORD = 'Pa$$w0rd';  
GO  

-- Setup permission for SignProcedureLogin to target database
USE TargetDatabase
GO
CREATE USER SignProcedureLogin FOR LOGIN SignProcedureLogin;  
GRANT SELECT ON SCHEMA::dbo TO SignProcedureLogin

所以现在我应该有这个设置

在此处输入图像描述

但是当我执行

USE SourceDatabase
GO
exec dbo.GetData

我得到了同样的错误

服务器主体“ServiceAccount”无法在当前安全上下文下访问数据库“TargetDatabase”。

我错过了什么?

更新

当我删除该with execute as条款时,它确实有效

-- Setup trustworthy
ALTER DATABASE SourceDatabase SET TRUSTWORTHY OFF;
ALTER DATABASE TargetDatabase SET TRUSTWORTHY OFF;

USE SourceDatabase
GO

create or alter procedure dbo.GetData
--with execute as 'ServiceAccount' 
as
begin
    select id, content from TargetDatabase.dbo.InterestingData
end

GRANT EXECUTE ON SCHEMA::dbo TO ServiceAccount

execute as login = 'ServiceAccount'
exec dbo.GetData
revert

但是仍然可以使用该with execute as子句以某种方式执行该过程吗?

sql-server
  • 1 1 个回答
  • 73 Views

1 个回答

  • Voted
  1. Best Answer
    Solomon Rutzky
    2022-11-05T08:21:00+08:002022-11-05T08:21:00+08:00

    您实际上与初始设置非常接近。你只是错过了 1 个步骤(并且有 2 个无关的步骤)。但是,在我解释如何使原始场景工作之前,我需要提及/警告原始场景并不理想,即使已修复也不应该使用,因为有一种更好更安全的方法:模块签名。我在对您的相关问题的回答中解释了原始设置解决的整体问题的首选解决方案:应该禁用可信赖选项,但有什么替代方案?.

    正如您所发现的(基于您在问题中的更新),问题是由于WITH EXECUTE AS ...在 proc 定义中使用了子句(即模拟)。这会将进程隔离到当前数据库。是的,启用TRUSTWORTHY(正如您最初尝试的那样)可以帮助克服这一点,但规则并不总是直截了当的。首先,TRUSTWORTHY仅影响启动进程的位置,因此没有理由TRUSTWORTHY在目标数据库中启用(这是两个无关步骤之一)。其次,当启用时TRUSTWORTHY,安全的焦点变成了当前数据库的所有者(即进程被启动的地方)。因此,问题在于是否SourceDatabaseOwner有权在 上充当身份验证器[TargetDatabase],而不是ServiceAccount(意思是,缺少的步骤是授予AUTHENTICATE-SourceDatabaseOwner这将需要SourceDatabaseOwner作为用户添加[TargetDatabase]- 而第二个无关步骤是授予AUTHENTICATE)ServiceAccount。

    这是一个工作演示,基于问题中的示例代码:

    最初设定

    /* -- Clean Up
    USE [master];
    DROP DATABASE SourceDatabase;
    DROP DATABASE TargetDatabase;
    
    DROP LOGIN SourceDatabaseOwner;
    DROP LOGIN TargetDatabaseOwner;
    DROP LOGIN ServiceAccount;
    */
    
    -- Create logins
    CREATE LOGIN SourceDatabaseOwner WITH PASSWORD = 'Pa$$w0rd';
    CREATE LOGIN TargetDatabaseOwner WITH PASSWORD = 'Pa$$w0rd';
    CREATE LOGIN ServiceAccount WITH PASSWORD = 'Pa$$w0rd';
    
    -- Create databases
    CREATE DATABASE SourceDatabase;
    CREATE DATABASE TargetDatabase;
    
    -- Setup trustworthy
    ALTER DATABASE SourceDatabase SET TRUSTWORTHY ON;
    --------ALTER DATABASE TargetDatabase SET TRUSTWORTHY ON; -- ???????????!!
    
    -- Setup database owners
    -- ( sp_changedbowner is deprecated )
    ALTER AUTHORIZATION ON DATABASE::[SourceDatabase] TO [SourceDatabaseOwner];
    ALTER AUTHORIZATION ON DATABASE::[TargetDatabase] TO [TargetDatabaseOwner];
    
    -- Add ServiceAccount to source database
    USE SourceDatabase
    CREATE USER ServiceAccount FOR LOGIN ServiceAccount;  
    GO
    
    -- Add ServiceAccount to target database
    USE TargetDatabase
    CREATE USER ServiceAccount FOR LOGIN ServiceAccount;  
    GO
    
    ---------- Enable ServiceAccount to authenticate to target database
    --------USE TargetDatabase
    --------GO
    --------GRANT AUTHENTICATE TO ServiceAccount; -- ???????????!!
    
    -- Grant permissions
    USE TargetDatabase
    GRANT SELECT ON SCHEMA::dbo TO ServiceAccount;
    GO
    
    -- Create table with data
    USE TargetDatabase;
    GO
    CREATE TABLE dbo.InterestingData (
        Id INT IDENTITY PRIMARY KEY,
        Content NVARCHAR(255)
    );
    INSERT INTO dbo.InterestingData (Content) VALUES ('Foo'), ('Bar');
    GO
    
    -- Create stored procedure executing under service account and
    -- accessing target database
    USE SourceDatabase;
    GO
    CREATE OR ALTER PROCEDURE dbo.GetData
    WITH EXECUTE AS 'ServiceAccount'
    AS
    BEGIN
        SELECT SESSION_USER AS [CurrentUser];
        SELECT Id, Content FROM TargetDatabase.dbo.InterestingData;
    END;
    GO
    

    测试

    -- Execution does not work under service account
    -- The server principal "ServiceAccount" is not able to access the database
    --     "TargetDatabase" under the current security context.
    USE [SourceDatabase];
    EXEC dbo.GetData;
    GO
    

    使固定 ...

    • 允许访问单个数据库

      USE [TargetDatabase];
      CREATE USER [SourceDatabaseOwner];
      GRANT AUTHENTICATE TO [SourceDatabaseOwner];
      

      — ??? —

    • 允许访问所有数据库

      USE [master];
      GRANT AUTHENTICATE SERVER TO [SourceDatabaseOwner];
      

    再次测试

    USE [SourceDatabase];
    EXEC dbo.GetData;
    -- Success!!
    GO
    

    如果出于某种原因,您真的需要/想要使用WITH EXECUTE AS(尽管通常没有理由这样做,因为模块签名允许 proc/function/trigger 始终如一地工作,而不管谁执行它),那么您仍然可以完成此操作无需TRUSTWORTHY通过模块签名启用。与您的原始设置一样,您再次完成了额外的模块签名步骤,但又缺少一个步骤和一个无关的步骤。模块签名将安全重点转移到证书(以及基于证书的登录和/或用户)。因此,缺少的步骤是不授予AUTHENTICATE基于证书的用户[TargetDatabase]. 而且,额外的步骤是为证书创建登录名,因为在这种情况下没有授予服务器级别的权限。

    这是一个工作演示,从上面继续演示(即不是独立的):

    从上面的演示中撤消使事情正常的额外步骤(以表明模块签名也修复了这种情况)

    USE [TargetDatabase];
    DROP USER [SourceDatabaseOwner];
    
    USE [master];
    REVOKE AUTHENTICATE SERVER TO [SourceDatabaseOwner];
    

    再次测试(验证 proc 不再有效)

    USE [SourceDatabase];
    EXEC dbo.GetData;
    -- error
    GO
    

    设置模块签名

    USE [SourceDatabase];
    
    -- Create certificate
    CREATE CERTIFICATE [SignProcedureCert]
        ENCRYPTION BY PASSWORD = 'Pa$$w0rd'
        WITH SUBJECT = 'Certificate for signing stored procedures';
    
    -- Sign the procedure
    ADD SIGNATURE TO dbo.GetData   
        BY CERTIFICATE SignProcedureCert  
        WITH PASSWORD = 'Pa$$w0rd';  
    GO  
    -----------------
    
    -- Copy certificate to target DB ( CERTENCODED in SQL Server 2012+ )
    DECLARE @Cert NVARCHAR(4000);
    SET @Cert = CONVERT(NVARCHAR(4000),
                        CERTENCODED(CERT_ID(N'SignProcedureCert')), 1);
    SELECT CERT_ID(N'SignProcedureCert'), @Cert;
    
    EXEC (N'USE [TargetDatabase];
    CREATE CERTIFICATE [SignProcedureCert] FROM BINARY = ' + @Cert);
    
    -- Create certificate-based User to allow access to DB
    USE [TargetDatabase];
    CREATE USER [SignProcedureCert] FROM CERTIFICATE [SignProcedureCert];
    
    -- Allow access to target object in target DB
    USE [TargetDatabase];
    GRANT SELECT ON SCHEMA::dbo TO [SignProcedureCert];
    GO
    

    再次测试(验证 proc 仍然不起作用)

    在相关问题(链接到顶部)中有效的模块签名设置在此处不起作用。

    USE [SourceDatabase];
    EXEC dbo.GetData;
    -- error
    GO
    

    问题是WITH EXECUTE ASproc 定义中的子句。

    使固定

    USE [TargetDatabase];
    GRANT AUTHENTICATE TO [SignProcedureCert];
    

    再次测试

    USE [SourceDatabase];
    SELECT SESSION_USER AS [CurrentUser];
    EXEC dbo.GetData;
    -- Success!!
    GO
    

    有关模块签名的更多信息,请访问我的网站:
    https ://ModuleSigning.Info/

    • 1

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

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

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

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

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