← Blog
Security

Compliance is Not Security: The HIPAA Compliant Illusion

Thien Nguyen · Jun 6, 2024

 

I was on a technical due diligence call with a CTO. He had already reviewed our materials beforehand.

"Look," he said, skipping the pleasantries. "Your slides say 'HIPAA compliant' and 'Security is in our DNA.' Every vendor says that. What I'm really worried about isn't an outside hacker, it's your own staff. Someone curious, or someone careless."

 

He leaned forward.

"How do you actually stop a doctor who is logged in, authenticated, gets curious, and pulls up the medical record of a patient who belongs to another doctor?"

 

He was right to ask. That is the real question.

 

He wasn't asking about a checklist. He was asking about architecture. His concern is the single biggest point of failure I see in "compliant" systems, and it stems from a design that is wrong from the ground up: "context-blindness."

 

Below is a deep dive into this problem, along with the specific architecture we use to solve it.

The flaw in "compliant" systems: A context-blind architecture

Most systems fail this test because they are built "happy-path" first. The "happy path" assumes the Doctor is a trusted entity by default. And then the architecture simply follows the path of least resistance.

 

The "compliant" checklist only requires:

  1. Encryption: AES-256 at rest, TLS in transit. Done.
  2. Authentication: User logged in via OAuth/SAML. Done.
  3. Roles (RBAC): The user's JWT token has Role: Doctor. Done.
  4. Logging: The access is written to a log file. Done.

 

The resulting API is highly predictable: GET /api/v1/patients/{patientId}

 

The "security" logic in the code, usually tucked into a single middleware layer, checks only one thing: "Does this user's token have the Doctor role?" If so, grant access.

 

This is context-blindness. It can confirm the user's role, but it cannot confirm their relationship to the data. It can't tell the difference between "their" patient and "any" patient.

 

And the problem doesn't stop at patient records. What about this one: GET /api/v1/doctors/{doctorId}/schedule

 

What stops dr_smith from querying dr_jones's work schedule to see every patient name on his day? This simple IDOR (Insecure Direct Object Reference) vulnerability is the direct consequence of a lazy, role-only architecture.

How to fix it at the architecture level: Context-aware security

You can't solve this by adding "more compliance." You have to fix it at the architecture level.

 

1. From RBAC to ABAC: "Who" vs. "Why"

 

RBAC (Role-Based Access Control) fails because it is static. A role doesn't understand relationships.

 

We apply ABAC (Attribute-Based Access Control). This model is "context-aware" and adapts to the moment.

  • RBAC asks: "Is this user a Doctor?"
  • ABAC asks: "Is this Doctor treating this Patient under an open case?"

 

The security policy is no longer just Allow if Role == Doctor. The policy, enforced right in the 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 for show. It is code, ideally enforced right at the API Gateway (like Kong or Apigee) or in a service mesh sidecar (like Istio), before the request even reaches the application.

 

And to answer the next question, about performance: that ActivePatientList is not a direct JOIN query running on every API call. Do that and the database falls over.

 

It is a denormalized, read-optimized cache (for example a Redis set), populated by the "Admissions" or "Scheduling" service. This cache is updated via events (for example PatientAdmitted, PatientDischarged) with an explicit TTL.

Security has to be fast enough, or developers will find a way to work around it.

 

2. From "audit log" to "investigation-ready observability"

Most "compliant" audit logs are just a noisy data lake, stood up only to please the auditor. They record something like:

[Timestamp] User 'dr_smith' accessed Patient '12345'.

 

This kind of log is useless for security. It's no different from the millions of other legitimate events. You can't build anomaly detection on top of it.

 

A proper security log has to record the "access decisions" to be genuinely observable.

 

The "Deny" log: When that curious doctor tries to access 12346 and our ABAC policy blocks it, here is what we record:

[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 into a file. It's a high-priority event, pushed straight to a SIEM system (like Splunk or Sentinel). Now you can write a simple rule: ALERT if Subject.UserID > 10 'Decision: DENIED' events in 1 minute. You've just caught an insider threat in progress.

 

The "break-the-glass" emergency log: What about emergencies? A doctor needs to access a record outside their normal context. A safe system must allow this through an explicit "break-the-glass" mechanism. But logging this situation matters even more:

[Timestamp] Subject 'dr_smith' accessed Resource '12347'.
Decision: GRANTED (EMERGENCY OVERRIDE).
Purpose: 'User-provided reason: Cardiac arrest in ER'.

 

This situation also triggers an alert, but a different kind: P2 - Post-Incident Review Required. The system is secure, usable, and most importantly, accountable.

The key technical questions

Compliance is the floor, not the ceiling.

 

Don't ask your partner whether they can "pass the checklist." You're not buying a checklist. You're buying an architecture that protects you even when the checklist fails.

 

Instead, ask them these questions:

  1. "Show me your authorization data model. Is it role-based, or attribute/relationship-based?"
  2. "How do you log an authorization failure differently from an authentication failure?"
  3. "How do you handle 'break-the-glass' emergency situations in your logging and alerting flow?"