Here’s the improved procedure: ALTER PROCEDURE pSecurity_Assign @ContactCode VARCHAR15, @LocationCode VARCHAR15, @SecurityLevel INT AS SET NOCOUNT ON; DECLARE @ContactID UNIQUEIDENTIFIE
Trang 1@LocationCode = ‘RDBMS’,
@SecurityLevel = 3;
Result:
Server: Msg 50000, Level 15, State 1, Procedure pSecurity_Assign, Line 30
Location: ‘RDBMS’ not found
Handling security-level updates
ThepSecurity_Assignprocedure used in the previous examples handles new security assignments
but fails to accept adjustments to an existing security setting
The following alteration to the procedure checks whether the security combination of contact and
loca-tion is already in theSecuritytable, and then performs the appropriateINSERTorUPDATE Security
permissions may be created or adjusted with the new version of the procedure and the same parameters
Here’s the improved procedure:
ALTER 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 @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 @LocationID IS NULL BEGIN;
RAISERROR (‘Location: "%s" not found’, 15,1,@LocationCode);
RETURN -100;
END;
Trang 2IS Update or Insert?
IF EXISTS(SELECT *
FROM dbo.Security WHERE ContactID = @ContactID
AND LocationID = @LocationID)
Update
BEGIN;
UPDATE dbo.Security
SET SecurityLevel = @SecurityLevel
WHERE ContactID = @ContactID
AND LocationID = @LocationID;
IF @@ERROR <> 0 RETURN -100;
END;
Insert
ELSE
BEGIN;
INSERT dbo.Security
(ContactID,LocationID, SecurityLevel) VALUES (@ContactID, @LocationID, @SecurityLevel);
IF @@ERROR <> 0 RETURN -100;
END;
RETURN;
The following script tests the new procedure’s ability to modify a security permission for a
contact/location combination The first command modifies contact 120’s security for
locationW:
EXEC pSecurity_Assign
@ContactCode = ‘120’,
@LocationCode = ‘W’,
@SecurityLevel = 2;
EXEC pSecurity_Fetch
@ContactCode = ‘120’;
Result:
ContactCode LocationCode SecurityLevel
The following two commands issue a new security permission and edit an existing security permission
The third command fetches the security permissions for contact code 120:
EXEC pSecurity_Assign
@ContactCode = ‘120’,
@LocationCode = ‘CH’,
@SecurityLevel = 1;
Trang 3EXEC pSecurity_Assign
@ContactCode = ‘120’,
@LocationCode = ‘W’,
@SecurityLevel = 3;
EXEC pSecurity_Fetch
@ContactCode = ‘120’;
Result:
ContactCode LocationCode SecurityLevel
Checking Permissions
The value of row-level security is in actually allowing or blocking reads and writes The following
procedures, functions, and triggers are examples demonstrating how to build row-level read/write
validation
The security-check stored procedure
The security-check stored procedure,p_SecurityCheck, is central to the row-based security system
It’s designed to return a true or false value for a security request for a user, a location, and a requested
security level
The procedure selects the security level of the user for the given location and then compares that
value with the value of the requested security level If the user’s permission level is sufficient, then a1
(indicating true) is returned; otherwise, a0(for false) is returned:
CREATE PROCEDURE p_SecurityCheck
@ContactCode VARCHAR(15),
@LocationCode VARCHAR(15),
@SecurityLevel INT,
@Approved BIT OUTPUT
AS SET NOCOUNT ON;
DECLARE @ActualLevel INT = 0;
SELECT @ActualLevel = 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 c.ContactCode = @ContactCode AND l.LocationCode = @LocationCode;
Trang 4IF @ActualLevel < @SecurityLevel
SET @Approved = CAST(0 AS bit);
ELSE
SET @Approved = CAST(1 AS bit);
RETURN 0;
The following batch calls thep_SecurityCheckprocedure and uses the@OKlocal variable to capture
the output parameter When testing this from the script on the web, try several different values Use the
pSecurity_Fetchprocedure to determine possible parameters The following code checks whether
contact code 118 has administrative privileges at the Charlotte warehouse:
DECLARE @OK BIT;
EXEC p_SecurityCheck
@ContactCode = ‘118’,
@LocationCode = ‘Clt’,
@SecurityLevel = 3,
@Approved = @OK OUTPUT;
SELECT @OK;
Result:
0
The security-check function
The security-check function,fSecurityCheck, includes the same logic as thepSecurity_Check
stored procedure The advantage of a function is that it can be used directly within anIF
com-mand without a local variable being used to store the output parameter The function uses the
same three input parameters as the stored-procedure version and the same internal logic, but it
returns the approved bit as the return of the function, rather than as an output parameter Here’s the
function’s code:
CREATE FUNCTION dbo.fSecurityCheck (
@ContactCode VACHAR(15),
@LocationCode VARCHAR(15),
@SecurityLevel INT)
RETURNS BIT
AS
BEGIN;
DECLARE @Approved BIT = CAST(0 AS bit);
IF (SELECT 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 c.ContactCode = @ContactCode
Trang 5AND l.LocationCode = @LocationCode) >= @SecurityLevel BEGIN;
SET @Approved = CAST(1 AS bit);
END;
RETURN @Approved;
END;
The next code fragment demonstrates how to call the function to test security within a stored
proce-dure If the function returns a0, then the user does not have sufficient security, and the procedure
terminates:
Check within a Procedure
IF dbo.fSecurityCheck( ‘118’, ‘Clt’, 3) = CAST(0 AS bit) BEGIN;
RAISERROR(‘Security Violation’, 16,1);
ROLLBACK TRANSACTION;
RETURN -100;
END;
Using the NT login
Some applications are designed so that the user logs in with the application, and, so far, the row-based
security code has assumed that the username is supplied to the procedures However, if the SQL Server
instance is using NT authentication, then the security routines can use that identification
Rather than request the contact code as a parameter, the security procedure or function can
automat-ically usesuser_sname(), the NT login, to identify the current user The login name (domain and
username) must be added to theContacttable Alternately, a secondary table could be created to hold
multiple logins per user Some wide-area networks require users to log in with different domain names
according to location, so aContactLogintable is a good idea
The following function is modified to check the user’s security based on his or her NT login and a
ContactLogintable The first query demonstrates retrieving the login within T-SQL code:
SELECT SUSER_SNAME();
Result:
-NOLI\Paul
The following code creates the secondary table to store the logins:
CREATE TABLE dbo.ContactLogin(
ContactLogin UNIQUEIDENTIFIER
PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),
ContactID UNIQUEIDENTIFIER NOT NULL
REFERENCES dbo.Contact(ContactID) ON DELETE CASCADE,
NTLogin NVARCHAR(128) UNIQUE CLUSTERED);
Trang 6With the table in place, a simpleINSERTwill populate a single row using my login so the code can
be tested:
INSERT dbo.ContactLogin (ContactID, NTLogin)
SELECT ContactID, ‘NOLI\Paul’
FROM dbo.Contact
WHERE ContactCode = 118;
Check the data:
SELECT c.ContactCode, cl.NTLogin
FROM dbo.Contact AS c
INNER JOIN ContactLogin AS cl
ON c.ContactID = cl.ContactID;
Result:
ContactCode NTLogin
-
The security-check function is modified to join theContactLogintable and to restrict the rows
returned to those that match the NT login name Because the contact code is no longer required, this
SELECTcan skip the contact table and join theSecuritytable directly with theContactLogin
table:
CREATE FUNCTION dbo.fSecurityCheckNT (
@LocationCode VARCHAR(15),
@SecurityLevel INT)
RETURNS BIT
AS
BEGIN;
DECLARE @Approved BIT = CAST(0 AS bit);
IF (SELECT s.SecurityLevel
FROM dbo.Security AS s
INNER JOIN dbo.Location AS l
ON s.LocationID = l.LocationID
INNER JOIN dbo.ContactLogin AS cl
ON s.ContactID = cl.ContactID
WHERE cl.NTLogin = suser_sname()
AND l.LocationCode = @LocationCode) >= @SecurityLevel
BEGIN;
SET @Approved = CAST(1 AS bit);
END;
RETURN @Approved;
END;
Trang 7To test the new function, the following code fragment repeats the security check performed in
the last section, but this time the user will be captured from the NT login instead of being passed to the
function:
IF dbo.fSecurityCheckNT(‘Clt’, 3) = 0
BEGIN;
RAISERROR(‘Security Violation’, 16,1);
ROLLBACK TRANSACTION;
RETURN;
END;
The function did not return an error, so I’m allowed to complete the procedure
The security-check trigger
The security-check stored procedure and function both work well when included within a stored
pro-cedure, such as theFETCH,ADDNEW,UPDATE, orDELETEprocedures mentioned in the beginning of
this chapter; but to implement row-based security in a database that allows access from views, ad-hoc
queries, or direct table DML statements, you must handle the row-based security with a trigger The
trig-ger can prevent updates, but it will not be able to check data reads If row-based security is a
require-ment for reads, then all reads must go through a stored procedure
The following trigger is similar to the security-check function It differs in that the trigger must allow
for multiple orders with potentially multiple locations The joins must match up[Order]rows
and their locations with the user’s security level for each location The join can go directly from the
ContactLogintable to theSecuritytable Because this is anINSERTandUPDATEtrigger, any
security level below2for any order being written will be rejected and a security-violation error will be
raised TheROLLBACK TRANSACTIONcommand will undo the original DML command that fired the
trigger and all other modifications made as part of the same transaction:
CREATE TRIGGER OrderSecurity ON dbo.[Order]
AFTER INSERT, UPDATE AS
IF @@ROWCOUNT = 0 RETURN;
IF EXISTS (
SELECT * FROM dbo.Security AS s INNER JOIN dbo.ContactLogin AS cl
ON s.ContactID = cl.ContactID INNER JOIN Inserted AS i
ON i.LocationID = s.LocationID
WHERE cl.NTLogin = suser_sname() AND s.SecurityLevel < 2 )
BEGIN;
RAISERROR(‘Security Violation’, 16,1);
ROLLBACK TRANSACTION;
END;
Trang 8SQL Server has a solid reputation for security, but it lacks row-based security If the database is well
architected with a carefully implemented abstraction layer, then adding a custom row-based security
schema is not difficult
This concludes Part VII, ‘‘Security,’’ which is so critical for production databases SQL Server security is
based on matching privileges between principals and securables, and a chapter was devoted to each side
of the equation
The third security chapter covered data cryptography — introduced in SQL Server 2005 and extended
with SQL Server 2008
From here, the book moves into Part VIII, ‘‘Monitoring and Auditing.’’ The sheer number of
technolo-gies and options available for monitoring and auditing has grown such that what was a single chapter in
SQL Server 2005 Bible grew into a 10-chapter part in this edition.
Trang 10Monitoring and
Auditing
IN THIS PART
Chapter 53
Data Audit Triggers
Chapter 54
Schema Audit Triggers
Chapter 55
Performance Monitor
Chapter 56
Tracing and Profiling
Chapter 57
Wait States
Chapter 58
Extended Events
Chapter 59
Change Tracking
Chapter 60
Change Data Capture
Chapter 61
SQL Audit
Chapter 62
Management Data Warehouse
Wow! SQL Server has seen an explosion
of monitoring and auditing technologies SQL Server 2000
and before offered these traditional monitoring technologies:
■ Trace and Profiler
■ System Monitor and Performance Monitor
■ DML triggers and custom audit trails
■ Wait states
SQL Server 2005 added:
■ Dynamic management views
■ DDL triggers (Logon triggers with SP2)
■ Event notification
■ SSMS reports
■ Performance Dashboard (downloadable add-on)
SQL Server 2008 doubles the core monitoring technologies with the
following:
■ Extended events
■ SQL Server auditing
■ Change tracking
■ Change Data Capture
■ Management Data Warehouse
■ Policy-Based Management (covered in Chapter 43)