Skip to content
ApexSutra

Healthcare

Audit trails that hold up

If your application can edit its own audit log, you do not have an audit trail. What actually makes access records defensible.

ApexSutra EngineeringUpdated 8 min read

There is a pattern we see in regulated systems that looks compliant and is not.

The application writes access events to an audit_log table. The table has good columns — actor, resource, action, timestamp. It is indexed. There is a dashboard. And the application's database user has UPDATE and DELETE on it.

That is not an audit trail. It is a log the audited party can rewrite.

The distinction matters exactly when the record matters: a subject access request, a regulator asking who viewed a file, a dispute about whether a record was altered. At that moment, "we have logs" is not the claim you need to make. The claim is "this record could not have been changed, and here is why."

What the rules actually require

HIPAA is less specific here than people expect. The Security Rule's audit-controls standard, 45 CFR §164.312(b), requires covered entities to "implement hardware, software, and/or procedural mechanisms that record and examine activity in information systems that contain or use electronic protected health information." It does not prescribe append-only storage, hash chains, or a retention period. The neighbouring integrity standard, §164.312(c)(1), asks you to protect ePHI "from improper alteration or destruction" — which is the requirement people tend to satisfy for patient records and forget for the audit table itself.

FDA-regulated contexts are far more explicit, and worth reading even if Part 11 does not apply to you. 21 CFR §11.10(e) requires "secure, computer-generated, time-stamped audit trails to independently record the date and time of operator entries and actions that create, modify, or delete electronic records," and adds the sentence that settles the design argument: "Record changes shall not obscure previously recorded information." It also requires the trail to be retained at least as long as the records it describes, and to be available for agency review.

That is the standard worth building to regardless of your regulator, because it is the one that matches what an investigation actually needs. For the mechanics of log management, NIST SP 800-92 remains the reference — note that its Revision 1 has been sitting in draft since 2023, so the 2006 publication is still the current final guidance.

What "could not have been changed" requires

Separate the write path from the application's privileges. The service that records events should not be the service that can remove them. In practice: a dedicated append-only store, or a table where the application role holds INSERT only and no UPDATE or DELETE grant exists. This is a database permission, not a code convention — code conventions are what change under deadline pressure.

Chain the entries. Each record includes a hash of its own contents plus the previous record's hash. Deleting or editing anything mid-chain breaks verification from that point forward. It does not make tampering impossible; it makes tampering detectable, which is the achievable property and the one that matters evidentially.

Hash-chained audit entries and how tampering surfaces

Three audit entries in sequence. Each stores the hash of the previous entry alongside its own. If entry 002 is altered after the fact, its recomputed hash no longer matches the prev_hash stored in entry 003, so verification fails from that point forward.

ENTRY 001actor: u4471prev: —hash: 9f2a…ENTRY 002actor: u4471prev: 9f2a…hash: c71b…ENTRY 003actor: u8802prev: c71b…hash: 4e6d…prev_hashmismatch↑ altered after the factverification fails from 003 onward

Editing entry 002 changes its hash, so the prev_hash stored in 003 no longer matches. The tampering is not prevented — it is made impossible to hide without rewriting every subsequent entry.

Record the read, not just the write. Most systems log mutations because mutations are what developers think of as events. In healthcare and finance, the question asked after the fact is almost always who looked at this. A trail with no read events cannot answer the question it exists for. If you are working in a FHIR estate, the AuditEvent resource already models this — including read access — and adopting its vocabulary saves you designing an event schema and arguing about it later.

Capture enough context to be useful later. Actor identity, the specific resource, the action, a trustworthy timestamp, and the reason or access basis if your domain has one. "User 4471 read patient 20293" is a start; "under an active care relationship, from this session" is what closes an enquiry.

Put side by side, the difference is not subtle:

"We have an audit log"An audit trail
Application role grantsINSERT, UPDATE, DELETEINSERT only
Tamper evidencenonehash chain breaks on edit
Events recordedmutationsreads and mutations
Timestamp sourceapplication serversone database clock
Locationoperational databaseseparate append-only store
Answer to "could this have changed?""nobody would do that""here is the failed verification"

Two decisions that cause trouble later

Putting audit events in the operational database with everything else. It couples audit retention to the schema you migrate most often, and it puts audit writes in contention with production traffic. A separate store is easier to grant narrowly and easier to retain for years.

Deriving the timestamp from application servers. Clock drift across instances produces sequences that are provably wrong, which is worse than an approximate sequence — it invites the argument that the whole record is unreliable. Use the database's transaction time, from one clock.

The cost, honestly

Read auditing generates a lot of rows. A clinician opening twenty records in a shift produces twenty events, every shift, for every user, retained for years.

That is a real cost, and it is why this needs deciding at design time rather than after: the storage strategy, the retention period, and the partitioning that keeps queries usable at volume are all architectural. Retrofitting append-only guarantees onto a mutable table with three years of data in it is a migration project, not a patch — and it is one of the clearer examples of technical debt as a gap between an original assumption and a current requirement, rather than as untidy code.

A useful test

Ask the team a single question: if someone with production database access wanted to remove an audit entry, what would stop them, and how would you know?

If the answer is "nobody would do that," the trail is a convention. If the answer is "the role has no delete grant, and the hash chain would fail verification at that point," it is a control.

The first answer is what most systems have. The second is what regulated systems are asked for.

Questions we get asked

Does HIPAA actually require a hash chain or append-only storage? No. §164.312(b) requires mechanisms that record and examine activity, and it is deliberately technology-neutral. The reason to build to the stricter standard is evidential rather than regulatory: when an incident is disputed, "our design makes alteration detectable" is a far stronger position than "our policy prohibits it." FDA's Part 11 does effectively require it for the records it covers.

Can we not just restrict access instead of making the log immutable? Access control and tamper evidence answer different questions. Restriction reduces who could alter the record; it cannot demonstrate afterwards that nobody did. Regulators and opposing counsel ask the second question. You want both, and the grant-level restriction is the cheaper half.

How long should we retain audit events? Part 11's rule — at least as long as the underlying records — is a sound default, and in healthcare that is usually driven by local medical-records retention law rather than by the security rule. Decide it before you choose a partitioning strategy, because retention determines whether time-based partitioning or an external store is the cheaper design.

Does this apply outside healthcare? The mechanics are identical anywhere the record is evidence: payments, lending, and trading systems have the same requirement under different rules, which is why it comes up in most of our fintech work as well as our healthcare projects. The domain changes the retention period and the access-basis field, not the design.

Keep reading

Facing one of these decisions right now?

A first conversation is free and useful even if you go elsewhere. You'll leave with an opinion you can act on.

Book a consultation