otrdiena, 2010. gada 5. oktobris

Datu drošība divu datu bāzu gadījumā

Piemēri datu izgūšana no citas datu bāzes izmantojot kādu no 4 variantiem- (1) veidojot lietotājus abās datu bāzēs, (2) izmantojot “Ownership Chaining”, (3) izmantojot “Execute As”, (4) izmantojot sertifikātus.


Par pamatu izmantots raksts: http://msdn.microsoft.com/en-us/library/cc837966.aspx
Apakšsadaļa: Using Signatures on Procedures to Grant Permissions
[2012-01-19 papildināts] vēl viena saite kur skatīties: Extending Database Impersonation by Using EXECUTE AS

Situācijas apraksts: vienā SQL Server ir vairākas datu bāzes. Daļa lietotāji lieto vienu datu bāzi, otra daļa lieto otru datu bāzi. Rodas nepieciešamība, ka pirmās datu bāzes lietotājiem jānolasa daļa datu no otras datu bāzes. Otrajā datu bāzē ir konfidenciāla informācija, tādēļ lietotājiem jāvar nolasīt tikai un vienīgi to, kas ir paredzēts.

Visu piemēru ilustrācijai, tiek izmantotas divas datu bāzes- TestDB1 un TestDB2. Lietotājs TestUser ir tikai pirmajā datu bāzē (TestDB1). Otrajā datu bāzē ir dbo.TestTable, no kuras lietotājam TestUser būs nepieciešamība nolasīt kolonnas TestTableID un LigumaNr. Šo objektu izveidei izmanto šādu skriptu:
Create Database TestDB1;
Create Database TestDB2;
go
use TestDB2
go
Create Table dbo.TestTable
(
TestTableID int primary key identity,
LigumaNr NVarChar(20),
LabsKlients bit default (1)
)
Go
Insert Into dbo.TestTable (LigumaNr, LabsKlients) Values ('LVL10-45234', 1)
Insert Into dbo.TestTable (LigumaNr, LabsKlients) Values ('LVL10-33453', 1)
Insert Into dbo.TestTable (LigumaNr, LabsKlients) Values ('LVL10-23355', 0)
Insert Into dbo.TestTable (LigumaNr, LabsKlients) Values ('LVL10-00224', 1)
Go
CREATE LOGIN [TestUser] WITH PASSWORD= N'Test' --ļoti ļoti slikta parole
Go
USE [TestDB1]
GO
CREATE USER [TestUser] FOR LOGIN [TestUser]

Risinājums 1
Izveidot TestDB2 datu bāzē jaunu skatu, kurā tiek atlasīta informācija, kas būs pieejama otras datu bāzes lietotājam. Pielikt lietotāju otrai datu bāzei un nodrošināt pieeju tikai un vienīgi izveidotajam skatam.

Situācijas simulācijai izpildam skriptus:
Use TestDB2
Go
Create View dbo.vw_SkatsPrieksDB1Lietotajiem
As
Select TestTableID, LigumaNr
From dbo.TestTable
Go
CREATE USER [TestUser] FOR LOGIN [TestUser]
Grant Select On dbo.vw_SkatsPrieksDB1Lietotajiem To TestUserLai pārbaudītu kā tas strādā, var Management Studio pieslēgties ar lietotāju TestUser un norādot paroli “Test” un pārliecināties, ka lietotājam tiešām ir tiesības tikai uz konkrēto skatu.
Vaicājums atgriezīs datus:
Select *
From TestDB2.dbo.vw_SkatsPrieksDB1
Lietotajiemnākamais vaicājums neizpildīsies (būs kļūdas paziņojums):
Select *
From TestDB2.dbo.TestTable
Risinājuma nav slikts, tomēr tam ir daudzi acīmredzami trūkumi:
* vienam lietotājam nav problēmu tādu risinājumu izveidot, tomēr ja jāstrādā ar daudziem lietotājiem, šādu realizāciju veidot ir laikietilpīgi, garlaicīgi un apgrūtinoši.
* ja katru datu bāzi uztur cits izstrādātājs, šis risinājums ir īpaši apgrūtinošs.

Šo risinājumu var mainīt tā, ka nodrošina uz konkrēto skatu lietotājam ‘Public’ tiesības. Tas nozīmētu to, ka katrs, kam ir piekļuve serverim, varētu lasīt attiecīgā skata datus. Šāds risinājums būtu pietiekami labs tikai tad, ja attiecīgie dati nav konfidenciāli (piemēram, klasifikatori, ko lasa no klasifikatoru datu bāzes).

Lai atstātu piemēru tādu, kāds tas bija sākuma stāvoklī, jāizpilda skripts:
Use TestDB2
Go
Drop User [TestUser]
Drop view dbo.vw_SkatsPrieksDB1Lietotajiem
Risinājums 2
Izmantot Cross DataBase Ownership Chaining (Bīstams SQL Server parametrs: "cross db ownership chaining").
Teroija (kā tas strādā):
http://technet.microsoft.com/en-us/library/ms188676%28SQL.90%29.aspx
Viens variants ir atļauj lai uz servera visās datu bāzēs varētu tikt izmantots “Cross DB Ownership Chaining”:
sp_configure 'cross db ownership chaining', 1
reconfigure
Otrs variants “Ownership Chaining” atļaut tikai datu bāzes līmenī (Pieslēdz visām datu bāzēm, kurām jāpiedalās procesā. Ja servera līmenī ir nokonfigurēts, šis nav jālieto): 
Alter DataBase TestDB1 SET DB_CHAINING ON
Alter DataBase TestDB2 SET DB_CHAINING ON
Galu galā izveidojam skatu TestDB1, piešķiram tiesības to lasīt lietotājam TestUser un pieliekam lietotāju TestDB2, nenodrošinot nekādas tiesības otrā datu bāzē:
Use TestDB1
Go
Create View dbo.vw_SkatsNemDatusNoDB2
As
Select TestTableID, LigumaNr
From TestDB2.dbo.TestTable
Go
Grant Select On dbo.vw_SkatsNemDatusNoDB2 To TestUser
Go
Use TestDB2
Go
CREATE USER [TestUser] FOR LOGIN [TestUser]
Lietotājs rezultātā varēs izpildīt vaicājumu:
Select *
From testdb1.dbo.vw_SkatsNemDatusNoDB2
Bet nevarēs atlasīt datus no tabulas
Select *
From testdb2.dbo.TestTable
Risinājums ir mulsinošs un sarežģīti lietojams. “Ownership Chaining” iesaka neizmantot, jo potenciāli var tikt izmantots kā drošības caurums (piemēram, ja es varu izveidot procedūru kā “dbo” vienā datu bāzē, tad es varu otrā datu bāzē dzēst objektus, ja tie pieder šim pašam īpašniekam).

* piezīme- risinājumā ir tā, ka lietotājs ir pielikts abās datu bāzēs. Alternatīvs risinājums ir datu bāzē, kurā ir tabula, no kuras vēlas iegūt datus atļaut guest lietotāju! Tad lietotāji, kuriem jānodrošina piekļuve pie datiem nav jāpieliek abās datu bāzēs. guest lietotājam nav jānodrošina nekādas tiesības attiecīgajā datu bāzē!

Lai atstātu piemēru tādu, kāds tas bija sākuma stāvoklī, jāizpilda skripts (Uzmanību! Servera konfigurāciju noteikti ieteiktu atgriezt iepriekšējā stāvoklī, ja vien šis piemērs netiek izmantots reālā vidē):
sp_configure 'cross db ownership chaining', 0
reconfigure
Alter DataBase TestDB2 SET DB_CHAINING OFF
Alter DataBase TestDB1 SET DB_CHAINING OFF
use TestDB1
Drop View dbo.vw_SkatsNemDatusNoDB2;
GoUse TestDB2
Go
Drop User [TestUser]
Risinājums 3
Izmantot TRUSTWORTHY datu bāzes parametru (msdn) un izmantot Execute As (msdn skaidrojums). Šis risinājums ir līdzīgs iepriekšējajam risinājumam. Lai simulētu situāciju izmantosim šādu skriptu:
Alter Database TestDB1 Set TrustWorthy ON
Go
Use TestDB1
Go
Create PROCEDURE dbo.Test
WITH EXECUTE AS Owner
AS
Select TestTableID, LigumaNr
From TestDB2.dbo.TestTable
go
Grant Exec On dbo.Test to TestUser
(TrustWorthy apraksts emuārā: http://blogs.msdn.com/b/sqlsecurity/archive)

Alternatīva procedūrai būtu izmantot funkcija, kas atgriež tabulu (inline funkcijas nav atļautas):
Use TestDB1
Go
Create FUNCTION dbo.fn_Test()
RETURNS @TestTable TABLE (TestTableID int, LigumaNr NVarChar(20))
WITH EXECUTE AS Owner
AS
BEGIN
    Insert @TestTable
       Select TestTableID, LigumaNr
       From TestDB2.dbo.TestTable
    RETURN;
END
Go
Grant Select On dbo.fn_Test to TestUser
Piemērs ka tas strādā procedūras gadījumā:
exec testdb1.dbo.Test
Un lietotāja definētas funkcijas gadījumā:
Select * From testdb1.dbo.fn_Test()
Joprojām tabulas datus atlasīt nebūs iespējams:
Select * From testdb2.dbo.TestTable
Risinājums principā nemaz nebūtu slikts, ja vien nebūtu jāieslēdz datu bāzes “Turthworthy” parametrs. Tāpat kā “Cross database ownership chaining” gadījumā, šīs opcijas ieslēgšana ir potenciāls drošības caurums. Vienīgais variants, kad kas tāds būtu pieļaujams, ir gadījumā, ja visas datu bāzes uzturat pats un tā tas vienmēr būs.
Konkrētajā piemērā risinājums strādā tāpēc, ka izveidoto objektu īpašnieks (owner) abos gadījumos- gan tabulai vienā datu bāzē, gan procedūrai/funkcijai otrā datu bāzē- ir viens un tas pats. Ja tā nebūtu, tad problēma, izmantojot šo pašu ideju, būtu jārisina savādāk. Piemēram, izveidojot lietotāju abās datu bāzēs, vienā datu bāzē šim lietotājam nodrošināt tiesības uz tabulām, savukārt otrā datu bāzē izpildīt funkciju kā šis pats lietotājs.

Pēc sevis satīrīt var ar skriptu:
Use TestDB1
drop function dbo.fn_Test
drop procedure dbo.Test
Alter Database TestDB1 Set TrustWorthy OFF
Risinājums 4
Izmantojot sertifikātus (Sertifikāti SQL Server) un parakstītas procedūras (ideja gluži kā DB Mail: Lietotāja tiesības izmantojot sertifikātus).
Risinājums sastāv no šādiem soļiem:
* Datu bāzē tiek izveidots sertifikāts, kas šajā gadījumā tiek šifrēts izmantojot datu bāzes “Master Key” (būtu iespējams neveidot “Master Key”).
* Tiek izveidota funkcija un tā tiek parakstīta izmantojot sertifikāta privāto atslēgu.
* Sertifikātam tiek noņemta privātā atslēga (jo ar šo sertifikātu nav plānots nevienu citu objektu parakstīt) un izveidota sertifikāta rezerves kopija.
* Otrā datu bāzē tiek izveidots sertifikāts no rezerver kopijas, tiek izveitos lietotājs priekš sertifikāta, kam tiek nodrošinātas tiesības uz vajadzīgajiem objektiem. Šeit nav nepieciešama privātā atslēga- mēs uzticamies sertifikāta īpašniekam (funkcijai, kas parakstīta ar privāto atslēgu)

Rezultātā pirmajā datu bāzē ir funkcija, kas parakstīta ar sertifikātu (tātad- sertifikāta īpašnieks uzticas tam, ko dara konkrētā funkcija- parakstot to ar privāto atslēgu) un otrajā datu bāzē sertifikātam izveidots lietotājs, kuram piešķirtas tiesības uz konkrētiem objektiem (jo mēs uzticamies sertifikāta īpašniekam).
Tātad šajā gadījumā mēs uzticamies nevis lietotājiem no citas datu bāzes, bet gan sertifikāta īpašniekam.

Skripts, kas izveido attiecīgo scenāriju:
USE [TestDB1]
Go
create master key encryption by password = 'slikta MK parole';
create certificate MansSertifikats with subject = 'Testējamies!!!!';
Go
Create FUNCTION dbo.fn_Test()
RETURNS @TestTable TABLE (TestTableID int, LigumaNr NVarChar(20))
AS
BEGIN
    Insert @TestTable
       Select TestTableID, LigumaNr
       From TestDB2.dbo.TestTable
    RETURN;
END
Go
add signature to dbo.fn_Test by certificate [MansSertifikats];
alter certificate [MansSertifikats] remove private key;
backup certificate [MansSertifikats] to file = 'MansSertifikats.cer';
Grant Select on dbo.fn_Test To TestUser;
Go
USE [TestDB2]
Go
create certificate [MansSertifikats] from file = 'MansSertifikats.cer';
CREATE USER [Test] FOR certificate [MansSertifikats]
Grant Select on dbo.TestTable To Test
Go
Risinājums ir labs ar to, ka netiek atvērti “drošības caurumi”, jo ar sertifikāta palīdzību tiek parakstīta (piemēra gadījumā) funkcija, kurai mēs uzticamies. Ja funkcija tiks mainīta, tad tā atkal jāparaksta. Sliktā lieta ir tā, ka nevar parakstīt skatu, kas padarītu šo risinājumu plašāk un ērtāk lietojamu.


Pēc sevis satīram:
Use TestDB1
go
drop master key
drop function dbo.fn_Test
drop certificate MansSertifikats
Go
Use TestDB2
Go
drop user [Test]
drop Certificate MansSertifikats
===
Lai izdzēstu piemēros izmantotās datu bāzes un lietotāju, jāizpilda skripts (datu bāzes nedrīkst tikt lietotas un lietotājs nedrīkst būt pieslēdzies):
Drop Database TestDB1;
Drop Database TestDB2;
drop login [TestUser]

Nav komentāru:

Ierakstīt komentāru