Chapter 14 · Security: Logins, Users, Roles, Permissions, Encryption, and Auditing

SQL Server Audit, Extended Events for Security, Row-Level Security, Dynamic Data Masking, and Hardening

Combine SQL Server Audit, Extended Events, Row-Level Security and Dynamic Data Masking with protected evidence, policy tests and practical hardening.

Advanced180–230 minutesAudit/RLS/DDM hardening labSQL Server 2025 CU7 · 17.0.4065.4Compatibility 170 · Developer/ExpressSSMS 22.8.2 · Last reviewed August 2026

Learning outcomes

Encryption and least privilege reduce what should happen; operations teams still need evidence about what did happen, while applications need row-by-row enforcement and controlled presentation of sensitive fields. ServiceHub will combine four different tools without pretending they are interchangeable: SQL Server Audit for security/compliance records, Extended Events for diagnostic telemetry, Row-Level Security (RLS) for database-enforced row filtering/blocking, and Dynamic Data Masking (DDM) for controlled display.

The chapter closes with hardening discipline: protect the audit target, patch the engine and drivers, reduce attack surface, test policies under impersonated users, and assume highly privileged administrators can change controls unless evidence is exported/protected outside their administrative boundary.

01

Distinguish SQL Server Audit, Extended Events, error logs, RLS and DDM by purpose and trust boundary.

02

Implement and test a session-context RLS filter/block policy with multi-tenant ServiceHub rows.

03

Demonstrate DDM output and why it is not authorization or encryption.

04

Inventory Audit/XEvent state and design protected audit-target retention.

05

Build a practical security-hardening and incident-verification checklist.

1. Pick the control whose semantics match the requirement

sql · record the security lab baseline before changing anything
SELECT SERVERPROPERTY('ProductVersion') AS product_version,       SERVERPROPERTY('ProductUpdateLevel') AS update_level,       SERVERPROPERTY('Edition') AS edition,       SERVERPROPERTY('EngineEdition') AS engine_edition,       SERVERPROPERTY('IsIntegratedSecurityOnly') AS windows_auth_only;GOSELECT name,value_in_useFROM sys.configurationsWHERE name IN  (N'contained database authentication',   N'column encryption enclave type',   N'clr enabled',N'xp_cmdshell');GOSELECT DB_NAME() AS database_name,       ORIGINAL_LOGIN() AS original_login,       SUSER_SNAME() AS execution_login,       USER_NAME() AS database_user;GO
sql · create the disposable ServiceHub security lab
USE master;GOIF DB_ID(N'ServiceHubSecurityLab') IS NULL  CREATE DATABASE ServiceHubSecurityLab;GOALTER DATABASE ServiceHubSecurityLab SET COMPATIBILITY_LEVEL = 170;GOUSE ServiceHubSecurityLab;GOIF SCHEMA_ID(N'ops') IS NULL EXEC(N'CREATE SCHEMA ops AUTHORIZATION dbo;');IF SCHEMA_ID(N'api') IS NULL EXEC(N'CREATE SCHEMA api AUTHORIZATION dbo;');IF SCHEMA_ID(N'sec') IS NULL EXEC(N'CREATE SCHEMA sec AUTHORIZATION dbo;');IF SCHEMA_ID(N'lab14') IS NULL EXEC(N'CREATE SCHEMA lab14 AUTHORIZATION dbo;');GOIF OBJECT_ID(N'ops.WorkOrderSecure',N'U') IS NULLBEGIN  CREATE TABLE ops.WorkOrderSecure  (    work_order_id bigint IDENTITY(14001,1) NOT NULL      CONSTRAINT PK_ops_WorkOrderSecure PRIMARY KEY,    tenant_code char(3) NOT NULL,    customer_code varchar(16) NOT NULL,    customer_name nvarchar(100) NOT NULL,    customer_email varchar(200) NOT NULL,    status varchar(16) NOT NULL,    priority tinyint NOT NULL,    amount decimal(12,2) NOT NULL,    opened_at datetime2(0) NOT NULL,    notes nvarchar(400) NULL,    CONSTRAINT CK_ops_WorkOrderSecure_tenant      CHECK (tenant_code IN ('N01','W02','E03')),    CONSTRAINT CK_ops_WorkOrderSecure_status      CHECK (status IN ('OPEN','ASSIGNED','CLOSED','ESCALATED')),    CONSTRAINT CK_ops_WorkOrderSecure_priority CHECK (priority BETWEEN 1 AND 5)  );  INSERT ops.WorkOrderSecure    (tenant_code,customer_code,customer_name,customer_email,status,priority,amount,opened_at,notes)  VALUES    ('N01','CUST-1401',N'Mina Rahimi','mina@example.test','OPEN',2,240.00,'2026-08-01T09:00:00',N'North queue'),    ('N01','CUST-1402',N'Owen Brooks','owen@example.test','ASSIGNED',3,480.00,'2026-08-01T10:15:00',N'North dispatch'),    ('W02','CUST-1403',N'Sara Chen','sara@example.test','ESCALATED',5,860.00,'2026-08-01T11:30:00',N'West escalation'),    ('W02','CUST-1404',N'Ali Reza','ali@example.test','OPEN',1,120.00,'2026-08-02T08:20:00',N'West queue'),    ('E03','CUST-1405',N'Nora Bell','nora@example.test','CLOSED',4,735.00,'2026-08-02T09:40:00',N'East closed'),    ('E03','CUST-1406',N'David Kim','david@example.test','OPEN',2,315.00,'2026-08-02T12:05:00',N'East queue');  CREATE INDEX IX_ops_WorkOrderSecure_tenant_status    ON ops.WorkOrderSecure(tenant_code,status,opened_at)    INCLUDE(customer_code,priority,amount);END;GO
Control Primary job What it does not guarantee
SQL Server Audit Durable security/compliance-oriented event capture to an audit target Does not prevent the action; privileged administrators can manage/tamper with audit configuration unless governance protects the boundary.
Extended Events Focused diagnostic/event telemetry with rich filtering and low-overhead designs Not automatically a compliance archive; events can expose sensitive SQL and retention depends on target configuration.
SQL error log / login auditing Operational server/login failure evidence Not a complete object/data-access audit trail.
Row-Level Security Database-enforced filter/block predicates per row Does not encrypt rows; privileged principals that can alter policy remain a governance risk.
Dynamic Data Masking Obscure returned values from users without UNMASK Does not change stored data, prevent inference, or replace SELECT authorization.

2. Row-Level Security makes tenant filtering part of database execution

RLS uses an inline table-valued function as a security predicate and binds it to a target table through a security policy. A filter predicate hides rows from reads and eligible modifications; block predicates reject writes that violate the rule. The application can pass a trusted tenant identifier in SESSION_CONTEXT, but the application must establish that value from authenticated identity—not from an untrusted request parameter.

sql · create a session-context RLS policy for the ServiceHub table
USE ServiceHubSecurityLab;GOIF USER_ID(N'lab14_rls_app') IS NULL CREATE USER lab14_rls_app WITHOUT LOGIN;GRANT SELECT,INSERT,UPDATE ON OBJECT::ops.WorkOrderSecure TO lab14_rls_app;GOCREATE OR ALTER FUNCTION sec.fn_tenant_access(@tenant_code char(3))RETURNS TABLEWITH SCHEMABINDINGASRETURN  SELECT 1 AS allowed  WHERE @tenant_code=CONVERT(char(3),SESSION_CONTEXT(N'tenant_code'));GOIF EXISTS (SELECT 1 FROM sys.security_policies WHERE name=N'WorkOrderTenantPolicy')  DROP SECURITY POLICY sec.WorkOrderTenantPolicy;GOCREATE SECURITY POLICY sec.WorkOrderTenantPolicy  ADD FILTER PREDICATE sec.fn_tenant_access(tenant_code)    ON ops.WorkOrderSecure,  ADD BLOCK PREDICATE sec.fn_tenant_access(tenant_code)    ON ops.WorkOrderSecure AFTER INSERT,  ADD BLOCK PREDICATE sec.fn_tenant_access(tenant_code)    ON ops.WorkOrderSecure AFTER UPDATEWITH (STATE=ON,SCHEMABINDING=ON);GO
sql · prove filtering and block-predicate behavior as the application user
EXECUTE AS USER=N'lab14_rls_app';EXEC sys.sp_set_session_context @key=N'tenant_code',@value=N'N01';SELECT tenant_code,work_order_id,customer_code,statusFROM ops.WorkOrderSecureORDER BY work_order_id; -- only N01 rowsGOBEGIN TRY  INSERT ops.WorkOrderSecure    (tenant_code,customer_code,customer_name,customer_email,status,priority,amount,opened_at,notes)  VALUES    ('W02','CUST-BLOCK',N'Blocked tenant','blocked@example.test','OPEN',1,50,SYSUTCDATETIME(),N'should fail');END TRYBEGIN CATCH  SELECT ERROR_NUMBER() AS blocked_error,ERROR_MESSAGE() AS blocked_message;END CATCH;GOREVERT;

RLS applies even to dbo/owners when the policy is enabled, although sufficiently privileged users can alter or disable the policy. That is why policy DDL and audit evidence matter. Review predicate performance too: a security predicate participates in every affected query and can change plans.

3. Dynamic Data Masking changes result presentation, not stored values

sql · create and test a masked contact table
USE ServiceHubSecurityLab;GODROP TABLE IF EXISTS lab14.MaskedContact;CREATE TABLE lab14.MaskedContact(  contact_id int IDENTITY(1,1) PRIMARY KEY,  display_name nvarchar(100) NOT NULL,  email varchar(200) MASKED WITH (FUNCTION='email()') NOT NULL,  phone varchar(30) MASKED WITH (FUNCTION='partial(0,"XXX-XXX-",4)') NOT NULL,  annual_value decimal(12,2) MASKED WITH (FUNCTION='default()') NOT NULL);INSERT lab14.MaskedContact(display_name,email,phone,annual_value)VALUES (N'Mina Rahimi','mina@example.test','555-010-1401',125000), (N'Owen Brooks','owen@example.test','555-010-1402',83000);GOIF USER_ID(N'lab14_masked_reader') IS NULL CREATE USER lab14_masked_reader WITHOUT LOGIN;GRANT SELECT ON OBJECT::lab14.MaskedContact TO lab14_masked_reader;GOEXECUTE AS USER=N'lab14_masked_reader';SELECT * FROM lab14.MaskedContact;REVERT;GOGRANT UNMASK ON OBJECT::lab14.MaskedContact TO lab14_masked_reader;EXECUTE AS USER=N'lab14_masked_reader';SELECT * FROM lab14.MaskedContact; -- original values now visibleREVERT;REVOKE UNMASK ON OBJECT::lab14.MaskedContact FROM lab14_masked_reader;GO

The first query should return masked presentations; the second should expose original values because UNMASK was granted. The stored rows never changed. Users with broad control permissions can see unmasked values, and users with ad-hoc query access can sometimes infer masked values with predicates.

Wrong approach: “DDM means the user cannot know the value.”

Microsoft explicitly documents inference/brute-force techniques against masked columns. DDM is useful for accidental-exposure reduction and application presentation; it is not a security boundary against a malicious user who has broad query access. Use permissions, RLS and appropriate encryption for that job.

4. SQL Server Audit is a security record; Extended Events is diagnostic instrumentation

sql · inventory existing Audit and Extended Events state
SELECT name,audit_guid,type_desc,on_failure_desc,queue_delay,is_state_enabledFROM sys.server_auditsORDER BY name;GOUSE ServiceHubSecurityLab;SELECT name,is_state_enabled,audit_guid,create_date,modify_dateFROM sys.database_audit_specificationsORDER BY name;GOSELECT name,startup_state,event_retention_mode_desc,       max_memory,max_dispatch_latencyFROM sys.server_event_sessionsORDER BY name;GO

A SQL Server Audit object owns an audit target; server or database audit specifications select action groups/actions to record. Targets can include binary audit files and, on Windows, event logs. Audit targets need ACLs, capacity, retention and off-host protection appropriate to the threat model. A principal who can alter Audit configuration is inside that trust boundary unless external monitoring detects changes.

sql · server/database Audit pattern — choose a protected local path before running
-- Administrative template. FILEPATH must already exist and be writable by-- the SQL Server service account, while unauthorized users cannot modify it.-- USE master;-- CREATE SERVER AUDIT ServiceHubSecurityAudit-- TO FILE (FILEPATH=N'<protected audit directory>',MAXSIZE=512 MB,MAX_ROLLOVER_FILES=20)-- WITH (QUEUE_DELAY=1000,ON_FAILURE=FAIL_OPERATION);-- ALTER SERVER AUDIT ServiceHubSecurityAudit WITH (STATE=ON);-- GO-- USE ServiceHubSecurityLab;-- CREATE DATABASE AUDIT SPECIFICATION ServiceHubDataAccessSpec-- FOR SERVER AUDIT ServiceHubSecurityAudit--   ADD (SELECT ON OBJECT::ops.WorkOrderSecure BY public),--   ADD (DATABASE_OBJECT_CHANGE_GROUP)-- WITH (STATE=ON);

ON_FAILURE is a business availability decision. CONTINUE can preserve service while losing required audit records; FAIL_OPERATION can preserve audit completeness while denying audited operations when the target is unavailable. Do not copy a compliance setting without agreeing which failure mode the organization accepts.

Extended Events is excellent for focused troubleshooting—login errors, permission failures, query context, deadlocks or security DDL—but session predicates and actions can capture sensitive statements and parameters. Keep collection narrow and protect XEL files like other sensitive telemetry.

5. Harden the instance around the controls, not just inside the database

Security is a lifecycle: current patches, supported drivers, least-privileged service accounts, restricted network listeners/firewalls, TLS validation, disabled unused features, reviewed logins/roles, protected backups/keys, controlled SQL Agent proxies, auditable policy changes and tested recovery. A secure database on an unpatched host with reusable administrator credentials is not secure.

sql · produce a compact hardening evidence snapshot
SELECT SERVERPROPERTY('ProductVersion') AS product_version,       SERVERPROPERTY('ProductUpdateLevel') AS update_level,       SERVERPROPERTY('Edition') AS edition,       SERVERPROPERTY('IsIntegratedSecurityOnly') AS windows_auth_only;GOSELECT name,value_in_useFROM sys.configurationsWHERE name IN (N'xp_cmdshell',N'clr enabled',N'contained database authentication',  N'column encryption enclave type',N'remote access')ORDER BY name;GOSELECT name,type_desc,is_disabled,create_date,modify_dateFROM sys.server_principalsWHERE type IN ('S','U','G','E','X') AND name NOT LIKE N'##%'ORDER BY is_disabled,name;GO

This snapshot is not a universal “secure if zero” checklist. For example, CLR can be justified, containment can be intentional, and remote administration requirements differ. The point is to make every enabled surface attributable to an owner and business requirement, then monitor drift.

6. Production judgment

Production judgment. Export or forward critical audit records so the people being audited cannot silently destroy the only copy. Test RLS as multiple identities and validate predicate performance. Treat DDM as presentation only. Preserve Extended Events with appropriate access controls and retention. Patch SQL Server and drivers on a defined cadence, re-check Microsoft security advisories and current CU/GDR branches, and rehearse key/audit recovery as part of disaster recovery.

Chapter cleanup and security-policy verification

sql · remove disposable lesson objects while leaving the lab database available for review
USE ServiceHubSecurityLab;GOIF EXISTS (SELECT 1 FROM sys.security_policies WHERE name=N'WorkOrderTenantPolicy')  DROP SECURITY POLICY sec.WorkOrderTenantPolicy;DROP FUNCTION IF EXISTS sec.fn_tenant_access;DROP TABLE IF EXISTS lab14.MaskedContact;IF USER_ID(N'lab14_rls_app') IS NOT NULL DROP USER lab14_rls_app;IF USER_ID(N'lab14_masked_reader') IS NOT NULL DROP USER lab14_masked_reader;GOSELECT name,type_desc FROM sys.database_principals WHERE name LIKE N'lab14_%';SELECT name FROM sys.security_policies WHERE name LIKE N'%ServiceHub%' OR name LIKE N'WorkOrder%';GO-- Optional final cleanup after the chapter:-- USE master;-- ALTER DATABASE ServiceHubSecurityLab SET SINGLE_USER WITH ROLLBACK IMMEDIATE;-- DROP DATABASE ServiceHubSecurityLab;

Check your understanding

  1. What is the main semantic difference between SQL Server Audit and Extended Events?
  2. How does an RLS filter predicate differ from a block predicate?
  3. Why must the application protect the tenant value placed in SESSION_CONTEXT?
  4. Why does DDM not replace SELECT permission or encryption?
  5. Why should important audit records be protected outside the routine SQL Server administrator boundary?
Review the answers

1. Audit is designed for security/compliance event recording through audit specifications and protected targets; Extended Events is general-purpose diagnostic telemetry whose scope/retention must be designed explicitly.

2. A filter predicate silently restricts which rows are visible/eligible to reads and certain modifications; a block predicate rejects writes that would violate the policy.

3. If a user can choose another tenant identifier freely, the predicate faithfully enforces the attacker-supplied value. The session context must be derived from authenticated/authorized application identity.

4. DDM only changes returned presentation for users lacking UNMASK. Stored values remain unchanged, privileged users can see them, and ad-hoc predicates can infer data.

5. Highly privileged SQL Server principals can alter audit configuration. External/independently protected retention makes tampering detectable and preserves evidence during compromise.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.