33. Security & User Management -- Controlling Access
Level: Advanced SQL Server for real projects
- SQL Server authentication (SQL, Windows)
- CREATE LOGIN and CREATE USER
- Database roles and permissions
- GRANT, REVOKE, DENY
- Column-level security
- Row-level security (RLS)
- Encryption (TDE, column encryption)
- Common security mistakes
- Best practices
Unrestricted access = data breach. Authentication and authorization protect data. This article teaches access control.
Why Security Matters for Backend Developers
Data protection required:
Students see everyone's grades (bad)
Teachers see student salaries (illegal)
Public sees fee amounts (data breach)
v
Logins + roles + permissions = controlled access
v
Each user sees only authorized data
Security prevents leaks.
Authentication: Login Creation
CREATE LOGIN (server-level):
-- SQL authentication (password)
CREATE LOGIN BackendAppUser WITH PASSWORD = 'SecureP@ssw0rd!';
-- Windows authentication
CREATE LOGIN [DOMAIN\BackendService] FROM WINDOWS;
-- Change password
ALTER LOGIN BackendAppUser WITH PASSWORD = 'NewSecureP@ss!';
-- Disable login
ALTER LOGIN BackendAppUser DISABLE;
LOGIN: Server access. One per database user.
Authorization: User & Role
CREATE USER (database-level):
-- Link login to user
CREATE USER BackendAppUser FOR LOGIN BackendAppUser;
-- Add to role
ALTER ROLE db_datareader ADD MEMBER BackendAppUser;
ALTER ROLE db_datawriter ADD MEMBER BackendAppUser;
USER: Database access (mapped to LOGIN). ROLES: Collections of permissions.
Example 1: API User with Limited Access
Create read-only user for reporting:
-- Server: Create login
CREATE LOGIN ReportUser WITH PASSWORD = 'Report@123!';
-- Database: Create user
CREATE USER ReportUser FOR LOGIN ReportUser;
-- Add to reader role (SELECT only)
ALTER ROLE db_datareader ADD MEMBER ReportUser;
-- Revoke write permissions
DENY INSERT, UPDATE, DELETE ON DATABASE::SchoolDB TO ReportUser;
-- Result: ReportUser can SELECT, not INSERT/UPDATE/DELETE
Minimal permissions. Read-only for reports.
Example 2: Teacher User (Role-based Access)
Create teacher user with specific permissions:
-- Create teacher role
CREATE ROLE TeacherRole;
-- Grant permissions
GRANT SELECT ON dbo.Student TO TeacherRole;
GRANT SELECT ON dbo.Exam TO TeacherRole;
GRANT SELECT ON dbo.ExamResult TO TeacherRole;
GRANT UPDATE ON dbo.ExamResult TO TeacherRole; -- Can update marks
GRANT INSERT ON dbo.ExamResult TO TeacherRole; -- Can insert results
-- Deny sensitive tables
DENY SELECT ON dbo.FeeAccount TO TeacherRole; -- Can't see fees
DENY SELECT ON dbo.User TO TeacherRole; -- Can't see passwords
-- Create teacher user
CREATE USER MrsRao FOR LOGIN MrsRao;
ALTER ROLE TeacherRole ADD MEMBER MrsRao;
Teachers: See students/exams/results, not fees/passwords.
Example 3: Row-Level Security (RLS)
Students see only own results:
-- Create security policy
CREATE FUNCTION dbo.rls_StudentFilter(@StudentId INT)
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN
SELECT 1 AS result
WHERE @StudentId = CAST(SESSION_CONTEXT(N'StudentId') AS INT)
OR CAST(SESSION_CONTEXT(N'Role') AS NVARCHAR(MAX)) = 'Admin';
-- Create policy
CREATE SECURITY POLICY rls_ExamResultFilter
ADD FILTER PREDICATE dbo.rls_StudentFilter(StudentId)
ON dbo.ExamResult;
-- Usage:
EXEC sp_set_session_context @key = 'StudentId', @value = 5;
EXEC sp_set_session_context @key = 'Role', @value = 'Student';
SELECT * FROM ExamResult; -- Only StudentId = 5's results
RLS: Student sees only own data (automatic filtering).
Example 4: Column-Level Security
Hide sensitive columns:
-- Create view hiding sensitive columns
CREATE VIEW StudentPublic AS
SELECT
StudentId,
Name,
ClassName,
RollNumber
-- OMIT: DateOfBirth, ParentPhone, Address (sensitive)
FROM Student;
-- Grant access to view, not table
GRANT SELECT ON dbo.StudentPublic TO [public];
DENY SELECT ON dbo.Student TO [public];
-- Users query view (safe columns):
SELECT * FROM StudentPublic; -- No sensitive data
View restricts access to sensitive columns.
Example 5: Transparent Data Encryption (TDE)
Encrypt entire database at rest:
-- Create master key
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'MasterKey@123!';
-- Create certificate
CREATE CERTIFICATE TDECert WITH SUBJECT = 'TDE Certificate';
-- Create database encryption key
CREATE DATABASE ENCRYPTION KEY WITH ALGORITHM = AES_256 ENCRYPTION BY SERVER CERTIFICATE TDECert;
-- Enable encryption
ALTER DATABASE SchoolDB SET ENCRYPTION ON;
-- Check status:
SELECT * FROM sys.dm_database_encryption_keys;
-- Result: encryption_state = 3 (encrypted)
TDE: Entire database encrypted on disk. Transparent to queries.
Example 6: Column Encryption
Encrypt sensitive columns:
-- Create key
CREATE COLUMN MASTER KEY CMK_PasswordKey
WITH (
KEY_STORE_PROVIDER_NAME = 'MSSQL_CERTIFICATE_STORE',
KEY_PATH = 'CurrentUser/My/ThumbprintHere'
);
-- Create encryption key
CREATE COLUMN ENCRYPTION KEY CEK_PasswordKey
WITH VALUES (
COLUMN_MASTER_KEY = CMK_PasswordKey,
ALGORITHM = 'RSA_OAEP',
ENCRYPTED_VALUE = 0x...
);
-- Encrypt column
ALTER TABLE User
ALTER COLUMN PasswordHash
ENCRYPTED WITH (
ENCRYPTION_TYPE = DETERMINISTIC,
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256',
COLUMN_ENCRYPTION_KEY = CEK_PasswordKey
);
-- Result: PasswordHash encrypted in database
Column encryption: Encrypt sensitive columns.
Example 7: Audit Logging
Track who accessed what:
-- Create audit
CREATE SERVER AUDIT AuditSchoolDB
TO FILE (FILEPATH = 'C:\Audits\');
-- Create database audit
CREATE DATABASE AUDIT SPECIFICATION AuditStudentAccess
FOR SERVER AUDIT AuditSchoolDB
ADD (SELECT ON dbo.Student BY PUBLIC) WITH (QUEUE_DELAY = 1000);
-- Enable
ALTER SERVER AUDIT AuditSchoolDB WITH (STATE = ON);
ALTER DATABASE AUDIT SPECIFICATION AuditStudentAccess WITH (STATE = ON);
-- Review audit log
SELECT * FROM fn_get_audit_file('C:\Audits\*', DEFAULT, DEFAULT);
Audit: Track who queried what and when.
Common Security Mistakes
Mistake 1: sa account used for applications
Wrong:
-- Application uses sa login
Connection string: Server=localhost; User=sa; Password=admin;
-- sa = full admin access, no audit trail
Result: No accountability. Any user can drop database.
Fix: Dedicated app user:
CREATE LOGIN AppUser WITH PASSWORD = '...';
ALTER ROLE db_datareader ADD MEMBER AppUser;
ALTER ROLE db_datawriter ADD MEMBER AppUser;
-- Only what app needs
Mistake 2: Weak passwords
Wrong:
CREATE LOGIN User1 WITH PASSWORD = '123456';
Result: Easy to crack.
Fix: Strong passwords:
CREATE LOGIN User1 WITH PASSWORD = 'SecureP@ss2024!';
-- Min 12 chars, uppercase, lowercase, numbers, symbols
Mistake 3: Hardcoded credentials
Wrong:
// Hardcoded in code
string conn = "Server=localhost; User=admin; Password=password;";
Result: Anyone reading source sees credentials.
Fix: Config file or vault:
// Read from secure location
string password = GetPasswordFromVault("DatabaseUser");
Mistake 4: No role-based access
Wrong:
-- All users get same permissions
ALTER ROLE db_datareader ADD MEMBER Everyone;
ALTER ROLE db_datawriter ADD MEMBER Everyone;
-- Teachers see student salaries, students see fees
Fix: Role-based:
CREATE ROLE TeacherRole;
GRANT SELECT ON Student TO TeacherRole;
DENY SELECT ON FeeAccount TO TeacherRole;
Mistake 5: Unencrypted sensitive data
Wrong:
-- Passwords stored in plain text
INSERT INTO User (PasswordHash) VALUES ('mypassword');
Result: Database breach = exposed passwords.
Fix: Encrypt or hash:
-- Use HASHBYTES or column encryption
INSERT INTO User (PasswordHash) VALUES (HASHBYTES('SHA2_256', 'mypassword'));
Best Practices for Security
- Use Windows auth when possible -- No passwords in apps
- Principle of least privilege -- Only needed permissions
- Strong passwords -- 12+ chars, mixed case, symbols
- Separate roles -- Admin, Teacher, Student
- Encrypt sensitive data -- TDE + column encryption
- Enable auditing -- Track access
- Regular backups -- Encrypted, off-site
- Update SQL Server -- Security patches
LOGIN: Server-level authentication.
CREATE LOGIN AppUser WITH PASSWORD = '...';
USER: Database-level, maps to LOGIN.
CREATE USER AppUser FOR LOGIN AppUser;
LOGIN: How you authenticate. USER: Which database.
Collections of permissions:
db_datareader -- SELECT only
db_datawriter -- INSERT/UPDATE/DELETE
db_owner -- Full access
db_ddladmin -- ALTER/CREATE
Add user to role: ALTER ROLE db_datareader ADD MEMBER User;
REVOKE: Remove permission (back to default).
REVOKE SELECT ON Student FROM TeacherRole;
DENY: Explicitly block (even if role has it).
DENY SELECT ON FeeAccount FROM TeacherRole;
-- Teacher can't see, even if db_datareader role allows
DENY overrides.
Automatic filtering by user:
Student 1: SELECT * FROM ExamResult -- Sees only own results
Student 2: SELECT * FROM ExamResult -- Sees only own results
RLS predicate enforces transparently.
Use for: Multi-tenant, data isolation.
No, only sensitive:
- Passwords (hash actually)
- Credit card numbers
- Personal data (SSN, phone)
Don't encrypt: IDs, status, dates (need to index/filter).
CREATE SERVER AUDIT MyAudit TO FILE (FILEPATH = 'C:\...');
CREATE DATABASE AUDIT SPECIFICATION MySpec
FOR SERVER AUDIT MyAudit
ADD (SELECT ON dbo.Student);
ALTER SERVER AUDIT MyAudit WITH (STATE = ON);
Tracks SELECT on Student. Review audit log periodically.
Use ChatGPT, Claude, or Copilot to go deeper on Security and access control in SQL Server. Try these prompts:
"Show how to create API user with SELECT/INSERT permissions only""Explain role-based access: teacher sees students, not fees""What's row-level security? Show student sees only own results""How do I encrypt sensitive columns (passwords, SSN)?""Quiz me: ask 5 questions about logins, roles, permissions, encryption"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.