Cri*_*ole 10 sql-server impersonation
我已经被赋予了为遗留数据库编写Web界面的任务,我们拥有所有用户都拥有数据库帐户并相应地分配角色(当用户执行某些操作时,我们都会在整个地方记录触发器,所有这些都基于user_name()).
为了使用任何远程现代的东西,并避免以纯文本形式存储用户的密码,我正在连接具有每个用户的模拟权限的应用程序级帐户,我正在尝试运行Execute As User=@username并Revert设置和重置执行运行任何SQL之前和之后的上下文.
不幸的是,连接池的reset_connection呼叫与我的连接混乱,并且它最终会抛出一些关于物理连接无效的令人讨厌的错误...
我可以通过不使用连接池来解决此错误.但是,我的应用程序用户需要疯狂的权限来实际执行模拟.此外,杀死连接池是一个无赖......
如何在不牺牲安全性或性能的情况下做到这一点?请记住,我无法改变我的用户拥有数据库登录的事实,而且我真的不会以可检索的方式存储用户密码.我是绕过连接池的唯一选择,因此我可以冒充(并使用sa用户,因此我有足够的权限来实际冒充某人)?
为了实现一种“假”委托而不需要对应用程序/数据库代码进行巨大更改,我建议使用context_info()将当前用户传输到数据库,并将对 的调用替换为user_name()对dbo.fn_user_name().
有关如何构建此解决方案的示例
我将创建一个函数 fn_user_name ,它将从连接上的 context_info() 中提取用户名,或者在没有可用上下文信息时返回 user_name() 。请注意,连接上下文是 128 字节的二进制文件。你放在那里的任何内容都将用零字符填充,为了解决这个问题,我用空格填充值。
create function dbo.fn_user_name()
returns sysname
as
begin
declare @user sysname = rtrim(convert(nvarchar(64), context_info()))
if @user is null
return user_name()
return @user
end
go
Run Code Online (Sandbox Code Playgroud)
现在您会发现替换代码中对 user_name() 的所有调用并将其替换为此函数。
这里有 2 个选项。或者您创建自己的 SqlConnection 类,或者创建一个工厂方法,该方法将返回一个打开的 sqlconnection,如下所示。工厂方法有一个问题,即您运行的每个查询都将是 2 个数据库调用。不过,这是要编写的最少代码。
public SqlConnection CreateConnection(string connectionString, string user)
{
var conn = new SqlConnection(connectionString);
using (var cmd = new SqlCommand(
@"declare @a varbinary(128) = convert(varbinary(128), @user + replicate(N' ', 64 - len(@user)))
set context_info @a", conn))
{
cmd.Parameters.Add("@user", SqlDbType.NVarChar, 64).Value = user;
conn.Open();
cmd.ExecuteNonQuery();
}
return conn;
}
Run Code Online (Sandbox Code Playgroud)
您可以将其用作:
using(var conn = CreateConnection(connectionString, user))
{
var cmd = new SqlCommand("select 1", conn);
return conn.ExecuteScalar()
}
Run Code Online (Sandbox Code Playgroud)
对于 SqlConnection 的替代版本,您将需要重载 DbConnection 并实现 SqlConnection 的所有方法。执行方法将在下面添加查询并传入用户名作为额外参数。
declare @a varbinary(128) = convert(varbinary(128), @user + replicate(N' ', 64 - len(@user)))
set context_info @a
Run Code Online (Sandbox Code Playgroud)
该类将用作:
using(var conn = new SqlContextInfoConnection(connectionString, user))
{
var cmd = new SqlCommand("select 1", conn);
conn.open;
return conn.ExecuteScalar()
}
Run Code Online (Sandbox Code Playgroud)
我个人会实现选项 2,因为它更接近普通 SqlConnection 的工作方式。