Using transparent data encryption requires a master key: USE master; go --CREATE MASTER KEY ENCRYPTION BY PASSWORD = ‘Pa@sW0rD’; go --CREATE CERTIFICATE SQLBibleCert WITH SUBJECT = ‘SQLB
Trang 1Part VII Security
CreditCardNumber, Expires FROM CCard
WHERE CustomerID = 7;
Result:
CCardID CustomerID CreditCardNumber Expires - - -
It’s a good practice to close the key after the transaction:
CLOSE SYMMETRIC KEY CCardKey
For most applications, you’ll want to encrypt the data as it goes into the database and decrypt it as it is
selected If you want to move the data to another server and decrypt it there, then both servers must
have identical keys To generate the same key on two servers, the key must be created with the same
algorithm,identity_value, andkey_phrase
Using asymmetric keys
Using asymmetric keys involves encrypting and decrypting with matching private and public keys
Generating an asymmetric key is similar to generating a symmetric key:
CREATE ASYMMETRIC KEY AsyKey WITH ALGORITHM = RSA_512 ENCRYPTION BY PASSWORD = ‘P@s$w0rD’;
SQL Server supports RSA_512, RSA_1024, and RSA_2048 (algorithms for public-key cryptography) as
possible asymmetric algorithms The difference is the bit length of the private key
Asymmetric keys can also be generated from existing key files:
CREATE ASYMMETRIC KEY AsyKey FROM FILE = ‘ C:\SQLServerBible\AsyKey.key’
ENCRYPTION BY PASSWORD = ‘P@s$w0rD’;
Encrypting and decrypting data with an asymmetric key is very similar to using symmetric keys except
that the key doesn’t need to be open in order to be used
Using certificates
Certificates are typically used to encrypt data over the web for HTTPS endpoints SQL Server includes
certificates, as they fit into some companies’ security standards Certificates are typically obtained from
a certificate authority such as VeriSign or Thawte
Trang 2Nielsen c51.tex V4 - 07/21/2009 3:41pm Page 1203
Data Cryptography 51
Transparent Data Encryption
Data encryption is great, but what if the thief simply steals the whole server? Few shops encrypt every
column With enough time and hacker’s utilities, eventually they will gain access to the disk Using a
hex editor, the data in the SQL Server data file is right there for anyone to read So while they might
not be able to run SQL queries, they can still read the raw data
If the data is encrypted using the methods discussed previously in the chapter, the data is still safe
Transparent data encryption is for the rest of the data in case of theft of the data file or backup file
On nice aspect of transparent data encryption (TDE) is that it’s completely transparent The Storage
Engine encrypts or decrypts the data as it’s being written to or read from the disk There’s no code
change required at any layer of code
Using transparent data encryption requires a master key:
USE master;
go
CREATE MASTER KEY ENCRYPTION BY PASSWORD = ‘Pa@sW0rD’;
go
CREATE CERTIFICATE SQLBibleCert WITH SUBJECT = ‘SQLBibleCert’
To encrypt a user database a database key is required:
USE AdventureWorks2008
go
CREATE DATABASE ENCRYPTION KEY
WITH ALGORITHM = AES_128
ENCRYPTION BY SERVER CERTIFICATE SQLBibleCert
With the database key created, the database can be encrypted or decrypted using either analter
command or Management Studio’s Database Properties➪ Options page:
ALTER DATABASE AdventureWorks2008
SET ENCRYPTION ON
If you have the time, download a freeware hex editor, take a look at your database file, then encrypt the
database and take a second look – it’s a cool experiment
If you use TDE, be absolutely certain to back up the encryption keys If you need to
restore a database that’s been encrypted using TDE, and you don’t have the encryption
key, your backups are worthless.
Summary
Cryptography can provide another level of security beyond authentication It converts normal readable
data that can be understood to data that cannot be understood In this way, the data cannot be used by
the wrong parties SQL Server gives you many options for cryptography and makes it easy to use
The next chapter continues the discussion of security by covering row-level security, which is another
necessary tool for keeping data safe
Trang 4Nielsen c52.tex V4 - 07/21/2009 3:43pm Page 1205
Row-Level Security
IN THIS CHAPTER Extending the abstraction layer for custom row-level security Components of row-level security
Assigning and checking permissions
SQL Server is excellent at vertical security (tables and columns), but it lacks
the ability to dynamically enforce row-level security Views, usingwith
check option, can provide a hard-coded form of row-level security, but
developing a row-based security schema for an entire database using dozens or
hundreds of views would create a maintenance headache
Enterprise databases often include data that is sensitive on a row level Consider
these four real-life business-security rules:
■ Material data, inventory-cost data, and production scheduling are owned
by a department and should not be available to those outside that
department However, the MRP system contains materials and inventory
tracking for all locations and all departments in the entire company
■ HR data for each employee must be available to only the HR
depart-ment and an employee’s direct supervisors
■ A companywide purchasing system permits only lumber buyers to
purchase lumber, and only hardware buyers to purchase hardware
■ Each bank branch should be able to read any customer’s data, but only
edit those customers who frequent that branch
I believe the best possible solution for these requirements is to build the security
into the abstraction layer
In Chapter 2, ‘‘Data Architecture,’’ I tried to make the case for database
encapsula-tion and a strong abstracencapsula-tion layer as a means toward database extensibility; but a
strong abstraction layer also enables the security objective:
‘‘The sixth database objective based on the Information Architecture
Prin-ciple is security For any organizational asset, the data must be secured
depending on its value and sensitivity
Trang 5Part VII Security
Security is enforced by enforcing a strong database abstraction layer for all data access and
permitting no direct ad-hoc SQL access to the raw tables.’’
Implementing a server-side code version of row-level security requires four components:
■ Security table: Can contain the list of users and their departments, or branches, with their appropriate read and write rights
■ Security procedure: Checks the user’s rights against the data being requested and returns a status of approved or denied
■ Fetch procedure: Checks the security procedure for permission to return the data
■ Triggers: Call the security procedure to check the user’s right to perform the DML statement
on the requested rows
To demonstrate this design, the following topics implement row-level security to theOBXKites
database Each employee in theContacttable can be granted read, write, or administrator privileges
for each location’s inventory and sales data With this row-based security scheme, security can be
checked by means of a stored procedure, function, NT login, and trigger
Although this is only an example of how to construct row-level security, the concepts here should help
you as you design and develop your own custom row-level security solution
This code is a work in progress To download the latest version, please check
www.sqlserverbible.com
The Security Table
Thesecuritytable serves as a many-to-many associative table (junction table) between thecontact
andlocationtables The security level determines the level of access:
0 (or no rows): 0 access 1: Read access
2: Write access 3: Admin access Alternately, three-bit columns could be used for read, write, and administrator rights, but the privileges
are cumulative, so an integer column seems appropriate
Thesecuritytable has two logical foreign keys The foreign key to thelocationtable is handled
by a standard foreign key constraint; however, the reference to thecontacttable should allow only
contacts who are flagged as employees, so a trigger is used to enforce that complex referential-integrity
requirement The security assignment is meaningless without its contact or location, so both foreign keys
are cascading deletes A constraint is applied to the security-level column to restrict entry to the valid
security codes (0–3), and a unique constraint ensures that a contact may have only one security code
per location:
USE OBXKites;
CREATE TABLE dbo.Security ( SecurityID UniqueIdentifier NOT NULL PRIMARY KEY NONCLUSTERED,
Trang 6Nielsen c52.tex V4 - 07/21/2009 3:43pm Page 1207
Row-Level Security 52
ContactID UniqueIdentifier NOT NULL
REFERENCES dbo.Contact(ContactID) ON DELETE CASCADE,
LocationID UniqueIdentifier NOT NULL
REFERENCES dbo.Location(LocationID) ON DELETE CASCADE,
SecurityLevel INT NOT NULL DEFAULT 0
);
The following three commands add the constraints to theSecuritytable:
CREATE TRIGGER ContactID_RI
ON dbo.Security
AFTER INSERT, UPDATE
AS
SET NOCOUNT ON;
IF EXISTS(SELECT *
FROM Inserted
INNER JOIN dbo.Contact
ON Inserted.ContactID = Contact.ContactID
WHERE Contact.ContactID IS NULL
OR IsEmployee = CAST(0 AS bit)) BEGIN;
RAISERROR
(‘Foreign Key Constraint: Security.ContactID’, 16, 1);
ROLLBACK TRANSACTION;
RETURN;
END;
GO
ALTER TABLE dbo.Security
ADD CONSTRAINT ValidSecurityCode CHECK
(SecurityLevel IN (0,1,2,3));
ALTER TABLE dbo.Security
ADD CONSTRAINT ContactLocation UNIQUE
(ContactID, LocationID);
Assigning Permissions
Implementing row-level security requires a set of basic admin procedures to set up and maintain the
security settings These procedures handle assigning security levels to users
Assigning security
In order for theSecuritytable to be viewed, the first procedure created ispSecurity_Fetch This
procedure returns all the row-based security permissions, or it can be restricted to return permissions for
a single user or a single location:
CREATE PROCEDURE pSecurity_Fetch
@LocationCode VARCHAR(15) = NULL,
@ContactCode VARCHAR(15) = NULL
AS
Trang 7Part VII Security
SET NOCOUNT ON;
SELECT c.ContactCode,
l.LocationCode, s.SecurityLevel FROM dbo.Security AS s INNER JOIN dbo.Contact AS c
ON s.ContactID = c.ContactID INNER JOIN dbo.Location AS l
ON s.LocationID = l.LocationID WHERE (l.LocationCode = @LocationCode
OR @LocationCode IS NULL) AND (c.ContactCode = @ContactCode
OR @ContactCode IS NULL);
Row-level security permissions are set by adding or altering rows in theSecuritytable, which
serves as a junction between contact and location In keeping with the theme of server-side code, the
pSecurity_Assignstored procedure assigns a security level to the contact/location combination
There’s nothing new about this procedure It accepts a contact code and location code, converts the
codes into GUID IDs, and then performs theINSERT:
CREATE PROCEDURE pSecurity_Assign
@ContactCode VARCHAR(15),
@LocationCode VARCHAR(15),
@SecurityLevel INT AS
SET NOCOUNT ON;
DECLARE
@ContactID UNIQUEIDENTIFIER,
@LocationID UNIQUEIDENTIFIER;
Get ContactID
SELECT @ContactID = ContactID
FROM dbo.Contact
WHERE ContactCode = @ContactCode;
IF @@ERROR <> 0 RETURN -100
IF @ContactID IS NULL BEGIN;
RAISERROR (‘Contact: "%s" not found’, 15,1,@ContactCode);
RETURN -100;
END;
Get LocationID
SELECT @LocationID = LocationID
FROM dbo.Location
WHERE LocationCode = @LocationCode;
IF @@ERROR <> 0 RETURN -100;
IF @LocationID IS NULL BEGIN;
Trang 8Nielsen c52.tex V4 - 07/21/2009 3:43pm Page 1209
Row-Level Security 52
RAISERROR
(‘Location: "%s" not found’, 15,1,@LocationCode);
RETURN -100;
END;
Insert
INSERT dbo.Security (ContactID,LocationID, SecurityLevel)
VALUES (@ContactID, @LocationID, @SecurityLevel);
IF @@ERROR <> 0 RETURN -100;
RETURN;
With thepSecurity_FetchandpSecurity_Assignstored procedures created, the following batch
adds some test data The first two queries return some valid data for the test:
SELECT ContactCode
FROM dbo.Contact
WHERE IsEmployee = CAST(1 AS bit);
Result:
ContactCode
-118
120
119
The next query returns valid locations:
SELECT LocationCode FROM dbo.Location;
Result:
LocationCode
-CH
Clt
ElC
JR
KH
W
Based on this data, the next four procedure calls assign security:
EXEC pSecurity_Assign
@ContactCode = ‘118’,
@LocationCode = ‘CH’,
@SecurityLevel = 3;
EXEC pSecurity_Assign
@ContactCode = ‘118’,
Trang 9Part VII Security
@LocationCode = ‘Clt’,
@SecurityLevel = 2;
EXEC pSecurity_Assign
@ContactCode = ‘118’,
@LocationCode = ‘Elc’,
@SecurityLevel = 1;
EXEC pSecurity_Assign
@ContactCode = ‘120’,
@LocationCode = ‘W’,
@SecurityLevel = 2;
The following two commands test the data inserts using thepSecurity_Fetchprocedure The first
test examines the security settings for the‘W’location:
EXEC pSecurity_Fetch @LocationCode = ‘W’;
Result:
ContactCode LocationCode SecurityLevel
The next batch examines the security setting for ‘‘Dave Boston’’ (contact code 118):
EXEC pSecurity_Fetch @ContactCode = ‘118’;
Result:
ContactCode LocationCode SecurityLevel
The row-based security schema includes several constraints The following commands test those
constraints using the stored procedures
Testing the unique constraint:
EXEC pSecurity_Assign
@ContactCode = ‘120’,
@LocationCode = ‘W’,
@SecurityLevel = 2;
Result:
Server: Msg 2627, Level 14, State 2, Procedure pSecurity_Assign, Line 35
Trang 10Nielsen c52.tex V4 - 07/21/2009 3:43pm Page 1211
Row-Level Security 52
Violation of UNIQUE KEY constraint ‘ContactLocation’.
Cannot insert duplicate key in object ‘Security’
The statement has been terminated
Testing the valid security-code check constraint:
EXEC pSecurity_Assign
@ContactCode = ‘118’,
@LocationCode = ‘W’,
@SecurityLevel = 5;
Result:
Server: Msg 547, Level 16, State 1,
Procedure pSecurity_Assign, Line 35
INSERT statement conflicted with COLUMN CHECK constraint
‘ValidSecurityCode’ The conflict occurred in database
‘OBXKites’, table ‘Security’, column ‘SecurityLevel’
The statement has been terminated
Testing the employees-only complex-business-rule trigger:
Select ContactCode FROM dbo.Contact WHERE IsEmployee = CAST(0 AS bit);
EXEC pSecurity_Assign
@ContactCode = ‘102’,
@LocationCode = ‘W’,
@SecurityLevel = 3;
Result:
Foreign Key Constraint: Security.ContactID
The next execution of the stored procedure tests the contact foreign key constraint and generates an
error because999is an invalid contact:
EXEC pSecurity_Assign
@ContactCode = ‘999’,
@LocationCode = ‘W’,
@SecurityLevel = 3;
Result:
Server: Msg 50000, Level 15, State 1, Procedure pSecurity_Assign,
Line 19
Contact: ‘999’ not found
Test the location-code foreign key constraint It’s also checked within the stored procedure:
EXEC pSecurity_Assign
@ContactCode = ‘118’,