Zugriffsansicht basierend auf einer Tabelle in einer anderen Datenbank ohne Konto in dieser anderen Datenbank

10

Ich habe eine Ansicht in Datenbank1 basierend auf Tabellen in Datenbank2 erstellt. Ich habe SELECTeinem Benutzer die Erlaubnis gegeben, der nur Zugriff auf database1 hat. Der Benutzer kann diese Ansicht nicht zum Laufen bringen, da er kein Konto in Datenbank2 hat. Wie kann ich dieses Problem lösen? Ich möchte kein Konto in database2 erstellen.

Tom
quelle
1
@mustaccio Nein, dies ist kein Duplikat dieser anderen Frage / Antwort, da sich diese Situation alle in derselben Datenbank befand und es sich bei dieser Frage um das Übergreifen von Datenbanken handelt. Standardmäßig ist das nicht erlaubt. Man müsste die datenbankübergreifende Eigentumsverkettung aktivieren, und das ist eine große Sicherheitslücke, die sich für einen so engen Bedarf öffnen muss.
Solomon Rutzky
1
@SolomonRutzky, ich würde DB_CHAINING nicht als "große Sicherheitslücke" bezeichnen. In typischen Produktionsumgebungen, in denen nur Sysadmin-Rollenmitglieder Objekte erstellen können, ist dies kein Problem. Das heißt, es sollte sorgfältig verwendet werden, wenn Mitglieder, die keine Systemadministratoren sind, über Kontrollberechtigungen für andere als die von ihnen besessenen Schemas verfügen.
Dan Guzman
@DanGuzman "Vertrau mir, alles wird immer nach Plan verlaufen" ist keine effektive Strategie. Nach dieser Logik besteht fast kein Risiko TRUSTWORTHY ON, die Anwendung als festzulegen oder anzumelden sa. DB Ownership Chaining und TRUSTWORTHYexistieren hauptsächlich, weil sie zu diesem Zeitpunkt die einzige Lösung sind. Aber jetzt, auch wenn es kein großes Risiko ist, ist DB Chaining sicherlich ein unnötiges Risiko, da das Signieren von Modulen nicht so schwierig ist. Und wenn man sich auf die DB-Verkettung verlässt und dann Dynamic SQL verwendet, ist es wahrscheinlicher TRUSTWORTHY ON, dass das Problem behoben wird, während es mit der Modulsignierung nicht kaputt gegangen wäre.
Solomon Rutzky
@SolomonRutzky, ich hätte vorgeschlagen, ein Modul zu signieren, wenn die Frage ein Modul anstelle einer Ansicht wäre. Meiner Meinung nach DB_CHAININGist dies nicht riskanter als die Verkettung von Besitzverhältnissen innerhalb der Datenbank, wenn sich die Objekte ohnehin in derselben Datenbank befinden sollten.
Dan Guzman
@DanGuzman Warum sollte man annehmen, dass "die Objekte sowieso in derselben Datenbank sein sollten"? Das OP hat nur das Gegenteil angegeben, da der DB-Zugriff getrennt bleiben soll. Da das OP eine Ansicht verwendet, habe ich eine TVF anstelle einer gespeicherten Prozedur vorgeschlagen. Dies bedeutet jedoch nicht, dass die weitere Verwendung einer Ansicht die beste Vorgehensweise ist. Es ist üblich, eine Änderung der Struktur und / oder des Ansatzes vorzuschlagen, wenn dies sinnvoll ist, wie dies hier der Fall ist. Trotzdem habe ich meiner Antwort eine optionale Wrapper-Ansicht hinzugefügt. Und angesichts der Tatsache, dass "dbo" am häufigsten alles besitzt, DB_CHAININGist dies ziemlich riskant.
Solomon Rutzky

Antworten:

9

Dies ist mit Module Signing auf sehr sichere Weise einfach zu bewerkstelligen. Dies ähnelt den folgenden zwei Antworten von mir, auch hier auf DBA.StackExchange, die Beispiele dafür geben:

Sicherheit gespeicherter Prozeduren mit Ausführung als, datenbankübergreifenden Abfragen und Modulsignatur

Berechtigungen in Triggern bei Verwendung datenbankübergreifender Zertifikate

Der Unterschied für diese spezielle Frage besteht darin, dass es sich um eine Ansicht handelt und Ansichten nicht signiert werden können. Daher müssen Sie die Ansicht in eine TVF-Funktion (Table-Valued Function) mit mehreren Anweisungen ändern, da diese signiert werden können und wie eine Ansicht aufgerufen werden können (also für den SELECTZugriff).

Der folgende Beispielcode zeigt genau das, was in der Frage angefordert wird, da der Login / Benutzer "RestrictedUser" nur Zugriff auf "DatabaseA" hat und dennoch Daten aus "DatabaseB" abrufen kann. Dies funktioniert nur durch Auswahl aus diesem einen TVF und nur aufgrund dessen, dass es signiert ist.

Durchführen diese Art von Cross-Datenbank - Zugriff , während immer noch eine Ansicht verwenden, und nicht dem Benutzer zusätzliche Berechtigungen zu geben, würde ermöglichen Cross-Database Ownership Chaining erfordern. Dies ist weitaus weniger sicher, da es für alle Objekte zwischen beiden Datenbanken vollständig offen ist (es kann nicht auf bestimmte Objekte und / oder Benutzer beschränkt werden). Durch die Modulsignierung kann nur diese eine TVF auf die SELECTDatenbank zugreifen (der Benutzer hat keine Berechtigung, die TVF hat dies), und Benutzer, die nicht über die TVF verfügen können, haben überhaupt keinen Zugriff auf "DatabaseB".

USE [master];

CREATE LOGIN [RestrictedUser] WITH PASSWORD = 'No way? Yes way!';
GO

---

USE [DatabaseA];

CREATE USER [RestrictedUser] FOR LOGIN [RestrictedUser];

GO
CREATE FUNCTION dbo.DataFromOtherDB()
RETURNS @Results TABLE ([SomeValue] INT)
AS
BEGIN
    INSERT INTO @Results ([SomeValue])
        SELECT [SomeValue]
        FROM   DatabaseB.dbo.LotsOfValues;

    RETURN;
END;
GO

GRANT SELECT ON dbo.[DataFromOtherDB] TO [RestrictedUser];
GO
---

USE [DatabaseB];

CREATE TABLE dbo.[LotsOfValues]
(
    [LotsOfValuesID] INT IDENTITY(1, 1) NOT NULL
        CONSTRAINT [PK_LotsOfValues] PRIMARY KEY,
    [SomeValue] INT
);

INSERT INTO dbo.[LotsOfValues] VALUES
    (1), (10), (100), (1000);
GO

---

USE [DatabaseA];

SELECT * FROM dbo.[DataFromOtherDB]();


EXECUTE AS LOGIN = 'RestrictedUser';

SELECT * FROM dbo.[DataFromOtherDB]();
/*
Msg 916, Level 14, State 1, Line XXXXX
The server principal "RestrictedUser" is not able to access
the database "DatabaseB" under the current security context.
*/

REVERT;

Alle oben genannten Schritte stellen die aktuelle Situation wieder her: Der Benutzer hat Zugriff auf DatabaseA, hat die Berechtigung, mit einem Objekt in DatabaseA zu interagieren, erhält jedoch einen Fehler, weil dieses Objekt in DatabaseA auf etwas in DatabaseB zugreift, auf das der Benutzer keinen Zugriff hat.

Mit den folgenden Schritten wird das Modul-Singen eingerichtet. Es macht folgendes:

  1. erstellt ein Zertifikat in DatabaseA
  2. Unterzeichnet die TVF mit dem Zertifikat
  3. Kopiert das Zertifikat (ohne den privaten Schlüssel) in die Datenbank B.
  4. Erstellt einen Benutzer in DatabaseB aus dem Zertifikat
  5. Erteilt SELECTdem zertifikatbasierten Benutzer die Berechtigung für die Tabelle in DatenbankB

Modul-Signatur-Setup:

CREATE CERTIFICATE [AccessOtherDB]
    ENCRYPTION BY PASSWORD = 'SomePassword'
    WITH SUBJECT = 'Used for accessing other DB',
    EXPIRY_DATE = '2099-12-31';

ADD SIGNATURE
    TO dbo.[DataFromOtherDB]
    BY CERTIFICATE [AccessOtherDB]
    WITH PASSWORD = 'SomePassword';

---
DECLARE @CertificatePublicKey NVARCHAR(MAX) =
            CONVERT(NVARCHAR(MAX), CERTENCODED(CERT_ID(N'AccessOtherDB')), 1);

SELECT @CertificatePublicKey AS [Cert / PublicKey]; -- debug

EXEC (N'USE [DatabaseB];
CREATE CERTIFICATE [AccessOtherDB] FROM BINARY = ' + @CertificatePublicKey + N';');
---


EXEC (N'
USE [DatabaseB];
CREATE USER [AccessOtherDbUser] FROM CERTIFICATE [AccessOtherDB];

GRANT SELECT ON dbo.[LotsOfValues] TO [AccessOtherDbUser];
');

---



EXECUTE AS LOGIN = 'RestrictedUser';

SELECT * FROM dbo.[DataFromOtherDB]();
-- Success!!

SELECT * FROM [DatabaseB].[dbo].[LotsOfValues];
/*
Msg 916, Level 14, State 1, Line XXXXX
The server principal "RestrictedUser" is not able to access
the database "DatabaseB" under the current security context.
*/

REVERT;

Wenn der Zugriff aus irgendeinem Grund über eine Ansicht erfolgen muss, können Sie einfach eine Ansicht erstellen, die aus der oben gezeigten TVF ausgewählt wird. In dieser Situation SELECTmuss der TVF kein Zugriff gewährt werden, sondern nur der Ansicht, wie unten gezeigt:

GO
CREATE VIEW dbo.[DataFromTVF]
AS
SELECT [SomeValue]
FROM   dbo.DataFromOtherDB();
GO

-- Remove direct access to the TVF as it is no longer needed:
REVOKE SELECT ON dbo.[DataFromOtherDB] FROM [RestrictedUser];

GRANT SELECT ON dbo.[DataFromTVF] TO [RestrictedUser];

Und jetzt zum Testen:

EXECUTE AS LOGIN = 'RestrictedUser';


SELECT * FROM dbo.[DataFromOtherDB]();
/*
Msg 229, Level 14, State 5, Line XXXXX
The SELECT permission was denied on the object 'DataFromOtherDB',
database 'DatabaseA', schema 'dbo'.
*/


SELECT * FROM [OwnershipChaining].[dbo].[LotsOfValues];
/*
Msg 916, Level 14, State 1, Line XXXXX
The server principal "RestrictedUser" is not able to access
the database "DatabaseB" under the current security context.
*/


SELECT * FROM dbo.[DataFromTVF];
-- Success!!


REVERT;

Weitere Informationen zum Modul-Signieren finden Sie unter: https://ModuleSigning.Info/

Solomon Rutzky
quelle
Werden die Zertifikate im Rahmen regulärer Sicherungen gesichert? Oder werden sie an anderer Stelle gespeichert und erfordern auch eine Dateisystemsicherung? Und was passiert, wenn Sie in einer niedrigeren Umgebung wiederherstellen, in der möglicherweise andere Kennwörter usw. verwendet werden?
Chris Aldrich
@ChrisAldrich In der hier gezeigten Verwendung wird es mit der Datenbank gesichert, da es vollständig in der Datenbank gespeichert ist. Wenn Sie verwenden, ALTER CERTIFICATE ... DROP PRIVATE KEYist der private Schlüssel nicht mehr vorhanden, wenn Sie ihn nicht zuerst mit BACKUP CERTIFICATE in einer Datei gesichert haben . Der öffentliche Schlüssel ist jedoch noch vorhanden sys.certificates. Und der öffentliche Schlüssel benötigt kein Passwort. Nur die Verwendung des privaten Schlüssels zum Signieren eines Moduls erfordert das Kennwort (das auf allen Servern gleich ist, anders als beim Schutz über den Hauptschlüssel).
Solomon Rutzky