SQL Server: можно ли синхронизировать GUID пользователей между двумя базами данных, если пользователи не привязаны к имени входа? - PullRequest
3 голосов
/ 11 декабря 2019

В нашем проекте для обеспечения безопасности и фильтрации на уровне строк используются пользователи базы данных SQL Server без учетных записей.

Мы внедрили очень простую форму зеркалирования, в которой транзакционная база данных резервируется и восстанавливается каждую ночь с точностью до секунды. «Зеркальная» копия. Веб-служба извлекает данные из зеркала.

Однако нам необходимо регистрировать запросы веб-службы в транзакционной базе данных, чтобы они не стирались при восстановлении следующего зеркала.

Мы попыталисьреализовать это, заменив таблицы журналов в зеркале синонимами, указывающими на «реальные» таблицы в транзакционной базе данных.

Однако попытки записи в синонимы неизменно заканчиваются сообщениями об ошибках, такими как:

Участник сервера "" не может получить доступ к базе данных "" в текущем контексте безопасности

Я предполагаю, что это происходит потому, что во время восстановления пользователи повторносоздал и назначил новые идентификаторы GUID?

Я нашел много ответов, в которых говорится о повторном подключении пользователя базы данных к имени входа в SQL Server с использованием sp_change_users_login или ALTER USER, но эти решения кажутся неприменимыми, поскольку у этих пользователей базы данных нет логинов.

Есть ли способ гарантировать, что пользователи в зеркальной базе данных распознаютсякак тот же пользователь в транзакционной базе данных, если его имена совпадают?

Спасибо!

Ответы [ 2 ]

4 голосов
/ 11 декабря 2019

Да, когда вы CREATE a USER (или LOGIN), вы можете определить SID при его создании:

USE DB1;
GO
CREATE USER SampleUser WITHOUT LOGIN WITH SID = 0x010500000000000903000000F759D99F7F71EC459908C0A30B39056C;

USE DB2;
GO
CREATE USER SampleUser WITHOUT LOGIN WITH SID = 0x010500000000000903000000F759D99F7F71EC459908C0A30B39056C;
1 голос
/ 13 декабря 2019

Есть ли какой-нибудь способ гарантировать, что пользователи в зеркальной базе данных распознаются как один и тот же пользователь в транзакционной базе данных, если их имена пользователей совпадают?

Ответ: Нет

Пользователи без имен входа являются участниками базы данных только . Чтобы все пользователи в разных БД были «едины», им необходимо «указать» на один и тот же принципал сервера: логин. Это логин, который «связывает» пользователей базы данных.

Я предполагаю, что это происходит потому, что во время восстановления пользователи воссоздаются и им назначаются новые GUID?

SID (идентификаторы безопасности) пользователей базы данных не изменяются при восстановлении БД. Тот факт, что sid не изменяются, но остаются прежними, требует действий, которые вы обнаружили (sp_change_users_login или ALTER USER) при восстановлении базы данных на другом сервере.

3 варианта действий между базами данных:

  1. перекрестная цепочка владения базой данных
  2. олицетворение
  3. подпись модуля

Я не могу сказать, будет ли работать перекрестная цепочка баз данных, так как у пользователей без входа в систему нетлюбая сущность безопасности, когда они покидают свою базу данных. За пределами своей базы данных пользователи, не имеющие логина, не имеют атрибутов безопасности, они теряют свою «идентичность».

С другой стороны, олицетворение и подписание модуля могут работать, поскольку атрибуты безопасности гарантированы другим принципалом.

Ниже приведен «упрощенный» документ для пользователей БД без входа в систему, выполняющий перекрестные действия БД с sql-модулями, подписанными асимметричным ключом. Есть много примеров онлайн для подписи модулей с сертификатами, и работа та же самая. В большинстве статей, которые вы найдете в Интернете, подписанные модули выполняются в рамках принципала сервера (exec as login, exec signature_module), в то время как пользователи без входа в систему не существуют на уровне сервера. Подписанные модули должны выполняться доверенным объектом для того, чтобы подпись вступила в силу (имя входа по определению является доверенным), а для пользователей без входа в систему их достоверность исходит из их собственной базы данных. Для этого для базы данных, в которой находятся пользователи без входа в систему, должно быть значение TRUSTWORTHY ON.

В Poc есть функция с многострочными табличными значениями со знаком в mirrordb, которая используется для выбора данных из транзакционной базы данных. Функция является основой для представления (представления не могут быть подписаны). Представление является целью DML. Фактические действия dml выполняются подписанными триггерами INSTEAD OF.

Вам необходимо сгенерировать и настроить путь к асимметричному ключу перед выполнением сценария.

Олицетворение может работать аналогичным образом (принятьудалите подписанные действия и создайте каждый модуль с помощью EXECUTE AS)

create database transactionaldb
go

create database mirrordb
go

--target table in transactionaldb
use transactionaldb
go

create table targettable
(
id int identity,
username nvarchar(128) default(user_name()),
somevalue varchar(10),
thedate datetime default(getdate())
)
go

--mirror db, userwithout login
use mirrordb
go

create user userwithoutlogin without login
go

--synonym, just for testing
create synonym targettablesynonym for transactionaldb..targettable
go
--for testing, grant permissions on synonym to loginless user
grant select, insert, update, delete on targettablesynonym to userwithoutlogin
go


--switch to loginless user and select from synonym
execute as user = 'userwithoutlogin'
go

select * from targettablesynonym --The server principal "S-1-2-3-4..." is not able to access the database "transactionaldb" under the current security context.
go
--revert
revert
go


/*
1) cross db ownership chaining 
2) impersonation
3) module signing (below)
*/

--module signing, asymmetric key
--create a strong key/name file using sn.exe  :  sn -k 2048 c:\testdata\asymkeytest.snk //use key size 2048 for sql2016 on.


--the same for transactionaldb
use transactionaldb
go

--master key transactionaldb
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'M@st3rkeyTransactional'
GO

CREATE ASYMMETRIC KEY asymkeytest  
    AUTHORIZATION dbo  
    FROM FILE = 'c:\testdata\asymkeytest.snk';  
GO

create user asymkeytransactionaldbuser from asymmetric key asymkeytest
go

--grant permissions on targettable to asymkeytransactionaldbuser
grant select, insert, update, delete on targettable to asymkeytransactionaldbuser;
go


use mirrordb
go

--master key mirrordb
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'M@st3rkeyMirror'
GO

CREATE ASYMMETRIC KEY asymkeytest  
    AUTHORIZATION dbo  
    FROM FILE = 'c:\testdata\asymkeytest.snk';  
GO

--a db user from the asymkey? not really needed
--create user asymkeymirrordbuser from asymmetric key asymkeytest
go


select is_master_key_encrypted_by_server, *
from sys.databases
where name in ('transactionaldb', 'mirrordb');
go


use mirrordb
go

--a function in mirror which reads the table from transactional
create or alter function dbo.fnreadtargettablefromtransactionaldb()
returns @result table
(
    id int,
    username nvarchar(128),
    somevalue varchar(10),
    thedate datetime
)
as
begin

    insert into @result(id, username, somevalue, thedate)
    select id, username, somevalue, thedate
    from transactionaldb.dbo.targettable;

    return;
end
go

--grant select on loginless user
grant select on fnreadtargettablefromtransactionaldb to userwithoutlogin;
go


--switch to loginless user and select from function
execute as user = 'userwithoutlogin'
go

select * from fnreadtargettablefromtransactionaldb(); --The server principal "S-1-2-3-4..." is not able to access the database "transactionaldb" under the current security context.
go
--revert
revert
go

--sign the function with the asymmetric key
add signature to fnreadtargettablefromtransactionaldb by ASYMMETRIC KEY asymkeytest;

--... after signing
execute as user = 'userwithoutlogin'
go

select * from fnreadtargettablefromtransactionaldb(); --The server principal "S-1-2-3-4..." is not able to access the database "transactionaldb" under the current security context.
go
--revert
revert
go

--the signed module/function is accessing the transactionaldb but it is NOT trusted
--it could be trusted if:
--   a. it was called in the context of a server principal (login, by definition it is trusted)
--   b. if the source db of the signed module is trustful

--make the source db of the signed module trustful
alter database mirrordb set trustworthy on;

--test again
execute as user = 'userwithoutlogin'
go

--synonym (needs permissions now, at the destination)
select * from targettablesynonym

--signed function, working, permissions from the asymmetric key
select * from fnreadtargettablefromtransactionaldb(); --works
go
--revert
revert
go


use mirrordb
go
--complete interface
create or alter view transactiontableview
as
select *
from fnreadtargettablefromtransactionaldb()
go

--view permissions
grant select, insert, update, delete on transactiontableview to userwithoutlogin;
go


--instead of insert trigger
create or alter trigger insteadofinsertonview on transactiontableview instead of insert
as
begin
    set nocount on;

    if not exists(select * from inserted)
    begin
        return;
    end

    insert into transactionaldb.dbo.targettable(username, somevalue, thedate)
    select user_name(), somevalue, thedate
    from inserted;
end
go
--sign
add signature to insteadofinsertonview by ASYMMETRIC KEY asymkeytest;
go


execute as user = 'userwithoutlogin'
go

    insert into transactiontableview(somevalue)
    select col
    from (values('one'), ('2'), ('3')) as t(col);

    select * from transactiontableview

go
--revert
revert
go


/***********************************************/

--check the security token of a signed module when db trustworthy is off
alter database mirrordb set trustworthy off;
go

--create a proc
create or alter procedure showsecuritytokens
as
begin

    select 'mirror_db' as dbname, *
    from mirrordb.sys.user_token
    union all
    select 'transactional_db' as dbname, *
    from transactionaldb.sys.user_token;
end
go

--sign the proc for accessing transactionaldb info
add signature to showsecuritytokens by ASYMMETRIC KEY asymkeytest;
go

--permissions
grant execute on showsecuritytokens to userwithoutlogin
go

--enable guest for transactionaldb
use transactionaldb
go

grant connect to guest
go


use mirrordb
go
--switch to loginless user
execute as user = 'userwithoutlogin'
go

exec showsecuritytokens
--when from an untrusted source: the signed module does not get the GRANTs of the asymmetric key (at the destination) 
/*
thedb_______________principal_id__name_________________________type___________________________usage
transactional_db____5_____________asymkeytransactionaldbuser___USER MAPPED TO ASYMMETRIC KEY__DENY ONLY    
*/
go
--revert
revert
go



--cleanup
/*
use master
go
drop database transactionaldb
go
drop database mirrordb
go
*/
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...