I was on a technical due diligence call with a CTO. He’d already reviewed our profile.
“Look,” he said, skipping the pleasantries. “Your deck says ‘HIPAA compliant’ and ‘Security is in Our DNA’. Every vendor says that. My real concern isn’t a hacker from outside; it’s an employee. Someone curious, or someone careless.”
He leaned in. “How do you actually stop a logged-in, authenticated doctor from getting curious and pulling the record of another doctor’s patient?”
He was right to ask. That’s the real question.
He wasn’t asking about a checklist. He was asking about architecture. His concern is the single biggest failure point I see in “compliant” systems, and it stems from a fundamental, “context-blind” design.
Here’s the deep dive on the problem and the specific architecture we use to solve it.
The “Compliant” Flaw: Context-Blind Architecture
Most systems fail this test because they are built “happy-path” first. The “happy path” assumes a Doctor is a trusted entity. The architecture then follows the path of least resistance.
The “compliant” checklist only requires:
- Encryption: AES-256 at-rest, TLS in-transit. Check.
- Authentication: The user is logged in via OAuth/SAML. Check.
- Role (RBAC): The user’s JWT token has
Role: Doctor. Check. - Logging: The access is written to a log file. Check.
The resulting API is predictable:
GET /api/v1/patients/{patientId}
The code’s “security” logic, often in a single middleware, just checks: “Does this user’s token have the Doctor role?” If yes, access is granted.
This is context-blind. It confirms the user’s role, but not their relationship to the data. It can’t tell the difference between “their” patient and “any” patient. This isn’t just limited to patient files. What about:
GET /api/v1/doctors/{doctorId}/schedule
What stops dr_smith from querying dr_jones’s schedule to see all his patient names for the day? This simple IDOR (Insecure Direct Object Reference) vulnerability is a direct result of a lazy, role-based architecture.
The Architectural Fix: Context-Aware Security
You cannot solve this by adding “more compliance.” You must fix the architecture.
1. From RBAC to ABAC: The “Who” vs. “Why”
RBAC (Role-Based Access Control) fails because it’s static. A role doesn’t understand relationships.
We enforce ABAC (Attribute-Based Access Control). This model is “context-aware” and dynamic.
- RBAC asks: “Is this user a Doctor?”
- ABAC asks: “Is this Doctor currently treating this Patient for an active case?”
The security policy isn’t just Allow if Role == Doctor. The policy, enforced in code, becomes:
'Allow 'Read' IF: Subject.Role == 'Doctor' AND Resource.PatientID is IN Subject.ActivePatientList AND Action.Purpose == 'ActiveTreatment'
Implementation Detail: This policy isn’t just documentation. It’s code, ideally enforced at the API Gateway (like Kong or Apigee) or in a service mesh sidecar (like Istio), before the request even hits the application.
And to answer the next logical question—performance—that ActivePatientList isn’t a live JOIN query on every API call. That would be a database killer. It’s a denormalized, read-optimized cache (e.g., a Redis set) that is populated by the “Admissions” or “Scheduling” service. The cache is updated via events (e.g., PatientAdmitted, PatientDischarged) with a clear TTL. Security must be performant, or developers will find a way to bypass it.
2. From “Audit Logs” to “Forensic-Ready Observability”
Most “compliant” audit logs are a data lake of noise, built only to satisfy an auditor. They say:
[Timestamp] User 'dr_smith' accessed Patient '12345'.
This is useless for security. It’s indistinguishable from a million other valid events. You can’t build anomaly detection on it.
A secure log must capture “access decisions” for true observability.
The “Denial” Log:
When that curious doctor tries to access 12346 and our ABAC policy blocks it, this is what we log:
[Timestamp] Subject 'dr_smith' (IP: x.x.x.x) attempted 'Read' on Resource '12346'.
Decision: DENIED.
Reason: 'Policy Violation: Resource.PatientID not found in Subject.ActivePatientList'.
This log doesn’t just go to a file. This is a high-priority event piped directly to a SIEM (like Splunk or Sentinel). You can now write a simple rule: ALERT if Subject.UserID > 10 'Decision: DENIED' events in 1 minute. You’ve just caught an active insider threat.
The “Break-the-Glass” Log: What about emergencies? A doctor needs to access a file outside their normal context. A secure system must allow this via an explicit “break-glass” function. But logging this is even more critical:
[Timestamp] Subject 'dr_smith' accessed Resource '12347'.
Decision: GRANTED (EMERGENCY OVERRIDE).
Purpose: 'User-provided reason: Cardiac arrest in ER'.
This also triggers an alert, but a different one: P2 - Post-Incident Review Required. The system is secure, usable, and—most importantly—accountable.
Key Technical Questions
Compliance is the floor, not the ceiling.
Don’t ask your partner if they can “pass a checklist.” You’re not buying a checklist. You’re buying an architecture that protects you when the checklist fails.
Ask them these questions instead:
- “Show me your data model for authorization. Is it role-based or attribute/relationship-based?”
- “How do you log an authorization failure versus an authentication failure?”
- “How do you handle ‘break-the-glass’ scenarios in your logging and alerting pipeline?”