Security and Auditing
Database security layers authentication (who), authorization (what), encryption (data protection), and auditing (accountability). No single control suffices — defense in depth is required for production and compliance (SOC 2, HIPAA, PCI-DSS).
Authentication Modes
-- Check current mode
SELECT SERVERPROPERTY('IsIntegratedSecurityOnly') AS windows_only;
-- 0 = Mixed Mode, 1 = Windows only
| Mode | Use Case |
|---|---|
| Windows Authentication | Domain-joined apps, SSPI, no password in connection string |
| SQL Server Authentication | Cross-platform apps, contained users, legacy |
| Azure AD | Azure SQL, managed identity |
Enable Mixed Mode only when required — Windows auth reduces credential sprawl.
Logins and Users
-- Server login
CREATE LOGIN app_api WITH PASSWORD = N'ComplexP@ssw0rd!2024' MUST_CHANGE, CHECK_POLICY = ON;
GO
USE MyApp;
CREATE USER app_api FOR LOGIN app_api;
ALTER ROLE db_datareader ADD MEMBER app_api;
ALTER ROLE db_datawriter ADD MEMBER app_api;
GRANT EXECUTE ON SCHEMA::dbo TO app_api;
Separate logins per application — never share sa or dbo across services.
Contained Database Users (No Server Login)
CREATE DATABASE MyApp CONTAINMENT = PARTIAL;
USE MyApp;
CREATE USER app_report WITH PASSWORD = N'StrongReportP@ss1';
ALTER ROLE db_datareader ADD MEMBER app_report;
Simplifies AG failover — users travel with database.
Least Privilege
-- Custom role
CREATE ROLE app_readonly;
GRANT SELECT ON SCHEMA::dbo TO app_readonly;
ALTER ROLE app_readonly ADD MEMBER analyst_jane;
-- Explicit deny overrides grant
DENY DELETE ON dbo.Orders TO app_api;
DENY ALTER ON SCHEMA::dbo TO app_api;
Review permissions regularly:
SELECT dp.name AS principal, dp.type_desc, o.name AS object_name, p.permission_name, p.state_desc
FROM sys.database_permissions p
INNER JOIN sys.database_principals dp ON p.grantee_principal_id = dp.principal_id
LEFT JOIN sys.objects o ON p.major_id = o.object_id
WHERE dp.name = N'app_api';
Server Roles — Avoid Over-Assignment
| Role | Risk |
|---|---|
sysadmin |
Full control — break-glass only |
db_owner |
Full database control |
securityadmin |
Can escalate privileges |
Grant minimum fixed/database roles; prefer schema-level grants.
Transparent Data Encryption (TDE)
Encrypts data and log files at rest — transparent to applications:
USE master;
CREATE MASTER KEY ENCRYPTION BY PASSWORD = N'MasterKeyStr0ng!';
CREATE CERTIFICATE TDE_Cert WITH SUBJECT = N'MyApp TDE Certificate';
USE MyApp;
CREATE DATABASE ENCRYPTION KEY
WITH ALGORITHM = AES_256
ENCRYPTION BY SERVER CERTIFICATE TDE_Cert;
ALTER DATABASE MyApp SET ENCRYPTION ON;
SELECT db.name, dek.encryption_state
FROM sys.databases db
LEFT JOIN sys.dm_database_encryption_keys dek ON db.database_id = dek.database_id
WHERE db.name = N'MyApp';
Backup certificates — without cert + key, encrypted backups are unrecoverable.
Always Encrypted
Client-side encryption — SQL Server never sees plaintext for protected columns:
-- Requires Column Master Key and Column Encryption Key setup in SSMS wizard
CREATE TABLE dbo.Patients (
PatientID INT PRIMARY KEY,
Name NVARCHAR(100),
SSN NVARCHAR(11) COLLATE Latin1_General_BIN2
ENCRYPTED WITH (
COLUMN_ENCRYPTION_KEY = CEK_Auto1,
ENCRYPTION_TYPE = DETERMINISTIC,
ALGORITHM = N'AEAD_AES_256_CBC_HMAC_SHA_256'
)
);
Application driver must support Always Encrypted — deterministic allows equality search; randomized is more secure.
Dynamic Data Masking
ALTER TABLE dbo.Users
ALTER COLUMN Email ADD MASKED WITH (FUNCTION = 'email()');
ALTER TABLE dbo.Users
ALTER COLUMN Phone ADD MASKED WITH (FUNCTION = 'partial(0,"XXX-XX-",4)');
-- Grant UNMASK for privileged roles only
GRANT UNMASK TO hr_admin;
Masking is not encryption — privileged users see full data.
Row-Level Security
CREATE FUNCTION dbo.fn_SecurityPredicate(@TenantID INT)
RETURNS TABLE WITH SCHEMABINDING
AS RETURN SELECT 1 AS fn_result
WHERE @TenantID = CAST(SESSION_CONTEXT(N'TenantID') AS INT);
CREATE SECURITY POLICY dbo.TenantFilter
ADD FILTER PREDICATE dbo.fn_SecurityPredicate(TenantID) ON dbo.Orders
WITH (STATE = ON);
App sets SESSION_CONTEXT after login — multi-tenant isolation in database.
SQL Server Audit
CREATE SERVER AUDIT ProductionAudit
TO FILE (FILEPATH = N'D:\Audit\', MAXSIZE = 100 MB, MAX_ROLLOVER_FILES = 20);
CREATE SERVER AUDIT SPECIFICATION ProductionAuditSpec
FOR SERVER AUDIT ProductionAudit
ADD (FAILED_LOGIN_GROUP),
ADD (SUCCESSFUL_LOGIN_GROUP),
ADD (DATABASE_CHANGE_GROUP);
ALTER SERVER AUDIT ProductionAudit WITH (STATE = ON);
ALTER SERVER AUDIT SPECIFICATION ProductionAuditSpec WITH (STATE = ON);
Database-level audit for sensitive tables:
CREATE DATABASE AUDIT SPECIFICATION MyAppAuditSpec
FOR SERVER AUDIT ProductionAudit
ADD (SELECT, INSERT, UPDATE, DELETE ON dbo.PaymentCards BY public);
Forward audit logs to SIEM (Splunk, Azure Monitor) for alerting.
Common Mistakes
| Mistake | Risk |
|---|---|
sa in connection strings |
Total compromise |
| Weak passwords, no policy | Brute force |
Excessive sysadmin membership |
Insider threat, malware spread |
| TDE without cert backup | Unrecoverable backups |
| Auditing disabled to save disk | Compliance failure after incident |
| Trusting masking as encryption | Data leak via privileged accounts |
Performance Tips
- TDE adds 3–5% CPU — negligible vs compliance requirement
- Audit selective events — full DML audit on hot tables adds I/O
- Index RLS predicate columns — filter must be sargable
- Use Windows auth — eliminates SQL password hash attacks
Production Scenario: PCI Scope Reduction
- Store card tokens only — no PAN in database
- Always Encrypted or external vault for any sensitive fields
- Separate network segment for SQL Server
- TDE on database files
- Audit failed logins + privilege changes
- Quarterly access review with
sp_helprotect/ catalog views
Troubleshooting
| Issue | Check |
|---|---|
| Login failed | Password policy, account locked, wrong database default |
| Cannot connect after AG failover | Orphan users — ALTER USER WITH LOGIN |
| TDE stuck encrypting | sys.dm_encryption_progress |
| Audit not writing | Disk space, audit spec STATE, permissions |
-- Fix orphan user
ALTER USER app_api WITH LOGIN = app_api;
Layer authentication, authorization, encryption, and auditing — document who has sysadmin and rotate credentials on schedule.