Documentation Index
Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
Use this file to discover all available pages before exploring further.
Track 5: Database Security for Healthcare
Databases are where PHI lives. This module covers comprehensive database security strategies from transparent data encryption to field-level encryption, secure query patterns, and HIPAA-compliant backup strategies.What You’ll Master
Transparent Data Encryption
Field-Level Encryption
Secure Query Patterns
Backup Security
Part 1: Database Security Architecture
Defense in Depth for Databases
HIPAA Requirements for Databases
| Requirement | Implementation | Verification Method |
|---|---|---|
| Access Control (§164.312(a)) | Role-based access, authentication | Access reviews, audit logs |
| Audit Controls (§164.312(b)) | Database activity monitoring | Log review, SIEM integration |
| Integrity (§164.312(c)) | Checksums, change tracking | Integrity verification scripts |
| Transmission Security (§164.312(e)) | TLS, encrypted connections | Network scans, config review |
| Encryption (§164.312(a)(2)(iv)) | TDE, field encryption | Encryption verification |
Part 2: PostgreSQL Security for Healthcare
Transparent Data Encryption with PostgreSQL
PostgreSQL Row-Level Security
PostgreSQL Audit Configuration
Part 3: MongoDB Security for Healthcare
MongoDB Client-Side Field Level Encryption (CSFLE)
MongoDB Role-Based Access Control
Part 4: Backup Security
HIPAA-Compliant Backup Strategy
Secure Backup Verification
Monthly Test Restores
- Data completeness
- Data integrity
- Application functionality
- Time to restore meets RTO
Part 5: Database Security Checklist
Pre-Deployment Checklist
Complete Security Checklist
Complete Security Checklist
- Database in private subnet (no public IP)
- Security group restricts to application servers only
- TLS 1.2+ required for all connections
- VPN or Direct Connect for admin access
- Strong password policy enforced
- No default/shared accounts
- Service accounts have minimal privileges
- Certificate authentication for admin accounts
- MFA for privileged database access
- Role-based access control implemented
- Least privilege enforced
- No direct table access (views/functions only)
- Row-level security for multi-tenant
- Encryption at rest enabled (TDE)
- Encryption in transit (TLS)
- Field-level encryption for sensitive PHI
- Encryption keys in KMS (not database)
- Key rotation configured
- All logins logged
- All queries logged (or sampled)
- PHI access specifically logged
- Privileged actions logged
- Logs shipped to SIEM
- Logs retained 6+ years
- Backups encrypted
- Backup keys different from data keys
- Geographic redundancy
- Retention meets HIPAA (6 years)
- Regular restore testing
- Backup access restricted
- Failed login alerts
- Privilege escalation alerts
- After-hours access alerts
- High-volume query alerts
- Schema change alerts
- Patch management process defined
- Security patches within 30 days
- Critical patches within 72 hours
- Patching tested in staging first
Practical Exercises
Exercise 1: Implement Field-Level Encryption
Exercise Instructions
Exercise Instructions
- Create a patients table with encrypted PHI fields
- Implement blind indexes for searchable fields (SSN, MRN)
- Create functions for secure insert, update, and query
- Test search functionality on encrypted data
- SQL for table creation
- Insert function with encryption
- Search function using blind indexes
- Update function
- Test queries demonstrating functionality
Exercise 2: Configure Row-Level Security
Exercise Instructions
Exercise Instructions
- Multiple providers (physicians, nurses)
- Patients assigned to specific providers
- Providers should only see their patients
- Break-glass for emergencies
- Create user context table
- Implement RLS policies on patient table
- Create break-glass function with audit
- Test with multiple users
- Provider A can see their patients
- Provider A cannot see Provider B’s patients
- Break-glass allows temporary access
- All access is logged
Exercise 3: Build Backup Automation
Exercise Instructions
Exercise Instructions
- Full backup weekly, incremental daily
- Encrypt with AES-256
- Upload to S3 with server-side encryption
- Verify backup integrity
- Rotate based on retention policy
- Backup script
- Verification script
- Retention management script
- Cron schedule
- Monitoring/alerting
Key Takeaways
Defense in Depth
Encrypt Everything
Least Privilege
Audit Everything
Next Steps
Encryption Deep Dive
Audit Logging
Interview Deep-Dive
You are designing the database layer for a new patient records system. The CTO asks: 'Should we use Transparent Data Encryption or field-level encryption?' What is your answer and why?
You are designing the database layer for a new patient records system. The CTO asks: 'Should we use Transparent Data Encryption or field-level encryption?' What is your answer and why?
- The answer is both, and they serve different purposes. TDE and field-level encryption protect against different threat models, and a defense-in-depth approach requires both layers.
- TDE encrypts the entire database at the storage layer — data files, WAL logs, temp files, and backups are encrypted on disk. It protects against the “stolen hard drive” scenario: if someone physically steals the server or gains access to the raw storage volume, they cannot read the data. But TDE is transparent to the application — any user or process that can authenticate to the database sees plaintext. A compromised database connection, a SQL injection attack, or an authorized DBA can read all data freely.
- Field-level encryption (application-layer encryption) encrypts specific PHI fields before they reach the database. The database stores ciphertext. Even a DBA with full database access sees encrypted blobs, not patient names or SSNs. This protects against insider threats, SQL injection, database credential compromise, and unauthorized queries.
- The tradeoff: TDE is easy to implement (flip a switch on RDS, configure encryption at rest) and has zero application code changes. Field-level encryption requires significant application code, breaks query functionality on encrypted fields, and adds complexity for key management. The performance impact of field-level encryption depends on your query patterns — if you frequently search or sort on encrypted fields, you need blind indexes or deterministic encryption.
- My recommendation: enable TDE as a baseline (it is nearly free in terms of effort), then apply field-level encryption to the highest-sensitivity PHI fields — SSN, diagnoses, mental health notes, substance abuse records, HIV status. Leave lower-sensitivity metadata (appointment dates, department codes) protected by TDE only. This gives you a strong security posture without encrypting every column and destroying query performance.
Your PostgreSQL database stores 2 million patient records with field-level encryption. Performance has degraded because every query requires decryption. How do you optimize this?
Your PostgreSQL database stores 2 million patient records with field-level encryption. Performance has degraded because every query requires decryption. How do you optimize this?
- The core performance problem is that decryption happens at query time, and if you decrypt fields you do not need, you waste CPU cycles and KMS API calls. The optimization strategy attacks this from multiple angles.
- First, use blind indexes for search operations. Instead of decrypting every SSN to find a match, precompute HMAC-SHA256 hashes at insert time and store them as indexed columns. Search queries hit the hash index (fast), then decrypt only the matching rows (minimal decryption). For 2 million records, this turns a full-table-decrypt into decrypting 1-10 rows.
- Second, implement selective decryption. Your API should only decrypt the fields the requesting user actually needs. If a billing specialist queries a patient, decrypt billing-related fields only — do not decrypt diagnoses or clinical notes they have no permission to see. This reduces decryption volume per request by 50-80% for most role types.
- Third, use connection-level DEK caching. When the application starts a session, fetch and decrypt the DEKs for the relevant patient context and cache them in application memory for the session duration. Do not call KMS to unwrap the DEK for every single field decryption — that would mean hundreds of KMS API calls per page load. Cache the unwrapped DEK for 5-15 minutes (matching your session timeout), then discard it from memory.
- Fourth, consider materialized views for reporting. If the compliance team needs aggregate statistics (patient counts by condition, average length of stay), compute those from decrypted data during off-peak hours and store the aggregated, de-identified results in a reporting table. The reporting table contains no PHI and can be queried without decryption.
- Fifth, partition the table by date or department. Queries that filter on unencrypted partition keys skip irrelevant partitions entirely, reducing the number of rows that even reach the decryption step.
Walk me through how you would implement Row-Level Security in PostgreSQL for a multi-tenant healthcare application where each clinic should only see its own patients.
Walk me through how you would implement Row-Level Security in PostgreSQL for a multi-tenant healthcare application where each clinic should only see its own patients.
- Row-Level Security (RLS) in PostgreSQL is the right tool here because it enforces access control at the database layer, providing defense in depth beyond application logic. Even if there is a bug in the application authorization code, RLS prevents cross-clinic data access.
- Implementation steps: First, enable RLS on all PHI-containing tables with ALTER TABLE patients ENABLE ROW LEVEL SECURITY. Second, create a security context mechanism — typically a session variable (SET LOCAL app.current_clinic_id = ‘clinic_123’) set by the application after authentication, or a security context table that maps database users to clinic IDs.
- Third, create the RLS policy: CREATE POLICY clinic_isolation ON patients FOR ALL TO app_role USING (clinic_id = current_setting(‘app.current_clinic_id’)). This policy ensures that any SELECT, INSERT, UPDATE, or DELETE on the patients table is automatically filtered to the current clinic’s records. A query like SELECT * FROM patients returns only that clinic’s patients — silently, without the application needing to add WHERE clinic_id = X to every query.
- Fourth, handle cross-clinic scenarios. Some users need cross-clinic access: system administrators, compliance officers doing audits, or providers with privileges at multiple clinics. Create separate policies for these roles: CREATE POLICY admin_access ON patients FOR SELECT TO admin_role USING (true). The admin_role sees all rows, but every access is audit-logged.
- Fifth, apply RLS to all related tables: encounters, clinical_notes, prescriptions, lab_results. A patient in clinic A should have all their associated records invisible to clinic B. This requires consistent clinic_id columns across the schema and policies on every table.
- Testing: RLS is powerful but silent. If a policy accidentally blocks legitimate access, the user sees zero rows with no error message. Comprehensive integration tests must verify that each role sees exactly the right data — no more, no less.