Security

Real offensive depth

Testing and defence led by a published security researcher with five CVEs, including a CVSS 9.1 Mirai botnet kill-switch.

All security →
Compliance

Audit-ready, fixed scope

SOC 2, ISO, and the Canadian privacy stack, run end to end with an independent auditor.

All frameworks →
Resources

Learn the space

Original research, free tools, and plain-language guides on security and compliance, from a published security researcher.

Read the blog →
Security

Logging and Audit Trails: Building for Compliance from Day One

Adding audit logging retroactively is one of the most painful engineering projects a team can take on. Every code path that touches sensitive data needs to be threaded with logging. Database schemas need new columns. Event pipelines need to be built. Old data is lost forever.

Building it from day one is barely additional work. Here is the model that scales from MVP to SOC 2 to SOC 2 + HIPAA + HITRUST without needing to be redone.

What audit logs are actually for

Three audiences. Each one wants different information from the same underlying data.

Compliance auditors. Who accessed what data, when, from where. Used during SOC 2, HIPAA, ISO 27001 audits to demonstrate access controls work as documented.

Security incident response. When something looks suspicious, the audit log is how you reconstruct what happened. Did the attacker access customer data? Which records? When?

Customer trust. Increasingly, enterprise customers want to export their own audit logs to their SIEM. Their security teams use this for their own monitoring and compliance.

The schema that works

Every audit log entry needs the same six fields:

  • Actor. Who did the thing. User ID, service account ID, system process. Include the auth method (password, SSO, API key).
  • Action. What they did. Use a controlled vocabulary, not free text. "user.login", "customer_record.read", "billing.update". Stable across versions.
  • Resource. What they did it to. Type plus ID. "customer:cust_abc123".
  • Outcome. Success or failure. If failure, the reason.
  • Timestamp. ISO 8601 with timezone, microsecond precision.
  • Context. IP, user agent, request ID, session ID. Whatever is useful for reconstructing the request.

Optional but valuable: the before/after state for mutations, the request ID linking the audit log to your application logs, a hash of the previous audit entry for tamper-evidence.

What to actually log

Not everything. Logging every read of every record drowns the signal in noise.

Always log:

  • Authentication events (login, logout, MFA challenge, password change).
  • Authorization changes (role granted, permission updated).
  • Access to sensitive data (customer PII, financial records, PHI).
  • Mutations to important state (customer record updates, billing changes, configuration changes).
  • Administrative actions (impersonation, data export, account deletion).
  • Failed authentication or authorization attempts.

Do not log every page view, every API call, every routine read. Those belong in application logs, not audit logs. The audit log is for the things that matter for compliance and security investigation.

Doing this for a deal? SOC 2 in 75 Days is our fixed-scope readiness track, with the price and the timeline published before you call us. See SOC 2 in 75 Days

Storage and retention

Audit logs go to a separate, append-only store. Not the same database as your application data. The two key properties are: hard to tamper with, easy to query for audit and incident purposes.

The pattern that works: write audit events to a queue (Kinesis, SQS, Kafka), have a consumer that writes them to an immutable store (S3 with object lock, dedicated audit log database with restricted write access). Index the events for query in something searchable (OpenSearch, ClickHouse, BigQuery).

Retention varies by framework:

  • SOC 2: typically 1 year minimum, your auditor will define.
  • HIPAA: 6 years for records related to PHI.
  • PCI DSS: 1 year minimum, 3 months immediately accessible.
  • GDPR: as long as needed for stated purpose, no fixed minimum.

The pragmatic answer for a multi-framework startup is 7 years to cover everything.

What to give customers

Enterprise customers will eventually ask for their audit logs. Plan for it. The minimum: an API or export that returns their audit events in a defined format (JSON or CSV), filterable by date range. The customer should not need to ask support to get their own audit log.

For larger enterprise customers, support real-time streaming to their SIEM. SCIM-style standards exist; pick one that fits your customer base.

Tamper evidence

For higher-trust environments, audit logs should be tamper-evident. The pragmatic approach: each entry includes a hash of the previous entry, forming a chain. Anyone with the chain can verify no entries were modified or deleted in the middle. This is cheap to implement and meaningful for security investigations. If you are building these to satisfy an auditor, make sure they produce the evidence the audit actually asks for, which is the point of SOC 2 evidence collection.

Building for compliance?

We help engineering teams design audit logging that satisfies SOC 2, HIPAA, and enterprise customer requirements without becoming a maintenance burden.

Get an architecture review

What an auditor actually does with your audit log

Engineers tend to imagine the auditor reading logs. That is not what happens. The auditor picks a control, asks you for the population of events that control governs, samples somewhere between five and twenty five items from that population depending on how often the control runs, and then asks you to produce the log entry proving each sampled item happened the way your policy says it does. For logical access, that usually means: here are twelve terminations from the audit period, show me the deprovisioning event for each one and the timestamp relative to the last day worked. For change management it means: here are twenty five production deploys, show me the approval and the identity of the person who merged.

The consequence is that your log design should be driven by sampling, not by storage. If an auditor can pull a sample and you need an engineer to write a bespoke query each time, you have built a data lake, not an evidence system. The teams that get through fieldwork fastest have a small set of saved queries, one per control, that take a date range and return a CSV with the actor, the action, the resource, and the timestamp already in human-readable form. That work takes a day and saves two weeks.

The completeness problem, which is the question that stalls audits

The single hardest question in an audit is not "show me an event." It is "how do I know this list is complete?" An auditor cannot rely on a CSV you exported, because you could have filtered rows out before sending it. This is called information produced by the entity, and it is where readiness engagements go sideways more often than any control failure. The auditor needs to see how the list was generated, not just the list.

There are three ways to satisfy it and you should pick one deliberately. Screen record the query being run against the source system with the filter criteria visible, so the auditor watches the population being produced. Or give the auditor read-only access to the log query interface and let them run it themselves, which is the cleanest option and the one that removes the argument entirely. Or produce a reconciliation: total count in the source system, total count in the export, and a documented explanation for the difference. Whichever you pick, agree it with the auditor in planning rather than in week six, because a rejected population means the whole test gets redone and your timeline slips by two weeks.

Clocks, ordering, and the boring failures that break a timeline

Two things quietly ruin audit logs in production. The first is clock drift. If your application servers, your database, and your identity provider are not all synchronized to the same time source, your reconstructed timeline during an incident will be internally inconsistent by seconds or minutes, and a forensics narrative that says the attacker read the record before they authenticated is worthless. Pin every host to NTP, log UTC exclusively, and let the presentation layer convert to local time. Never store local time in the log itself, because the first daylight saving transition will give you an hour where events appear twice with no way to disambiguate them.

The second is ordering. Timestamps at millisecond precision collide more than people expect under load, and once you introduce a queue between the emitter and the store, arrival order stops matching event order. Carry a monotonic counter per emitter alongside the timestamp so that ties break deterministically. If you are chaining hashes for tamper evidence, you need this anyway, because a chain built on arrival order in a distributed system will produce different chains on replay and destroy the property you built it for.

Who can delete an audit log, and how you prove they cannot

Every auditor with security experience asks the same follow-up: can the people whose actions are being logged also modify the log? If your platform team has administrative access to the audit store, the honest answer is yes, and the control weakens accordingly. This is where object lock and separated accounts earn their keep. Writing audit events into an S3 bucket in a separate AWS account, under a compliance-mode object lock with a retention period, means that even an account administrator in the primary account cannot alter the record, and even the root user in the log account cannot shorten the lock.

Compliance mode is unforgiving by design, so test the retention period against your storage budget first. A team that sets a seven year compliance lock on a verbose stream cannot delete a single object for seven years, including the four terabytes they wrote during a debug loop. Governance mode with alerting on the bypass permission is the reasonable middle for most companies, paired with a quarterly review of who holds that permission. Hash chaining is a detection mechanism rather than a prevention mechanism: it tells you the record was altered, it does not stop the alteration.

Personal data inside the audit log, and the deletion conflict

Audit logs contain IP addresses, user identifiers, email addresses, and sometimes the before and after values of records that themselves contain personal information. Under PIPEDA and under Quebec Law 25, an individual can request deletion, and Law 25 in particular gives a right to de-indexing and cessation of dissemination that people apply to logs without thinking through the consequences. You then have a direct conflict: your immutable, object-locked audit store cannot honor a deletion request, and your legal team has promised one.

Resolve this at design time with two decisions. First, keep the audit log referential rather than descriptive: store the subject identifier and the resource identifier, not the person's name, address, or the payload of the record they touched. If the identifier is a surrogate key and the mapping table is deletable, deleting the mapping renders the log entries non-identifying without touching the immutable store. Second, document your retention and the legal basis for it in your privacy notice, because retention necessary to meet a legal or contractual obligation is a defensible position, and security logging generally qualifies. What is not defensible is discovering the conflict when the first request arrives and improvising an answer under a thirty day clock.

Where the bill actually comes from

Audit logging costs are almost never storage. Compressed JSON events at a few hundred bytes each are cheap even at hundreds of millions per year. The bill comes from ingest and indexing. Most log platforms price on data ingested per day and retained index size, so one badly designed event that fires on every request and carries a full request body can multiply your monthly cost by an order of magnitude overnight. We have seen a single debug field added in a Friday deploy add four figures to a monthly bill before anyone noticed on the Tuesday.

The controls that keep this sane are unglamorous. Cap the event payload size at emission and truncate with a marker rather than letting a large blob through. Keep the searchable index short, thirty to ninety days, and tier everything older to object storage that you can rehydrate if an investigation or an audit needs it. Alert on ingest volume per event type so a cardinality explosion pages someone the same day, and review the event vocabulary quarterly.

What happens when the pipeline drops events

It will drop events. A queue consumer will fall behind during a traffic spike, a deploy will restart a writer mid-batch, or a downstream store will rate limit you. The question an auditor asks is not whether you had a gap. It is whether you knew about the gap, how long it lasted, and what you did. A gap you detected and documented is an observation. A gap you did not notice, discovered by the auditor comparing your log count against your application metrics, is a control deficiency and possibly a qualified opinion.

Build the detection before you need it. Emit a heartbeat event per service every minute and alert when heartbeats stop, which catches silent pipeline death that volume-based alerting misses on a low-traffic weekend. Decide the failure posture explicitly: if the audit pipeline is unavailable, does the request proceed unlogged or does it fail? For authentication and administrative actions in a regulated environment, failing closed is the right answer and you should be able to say so in your policy. For routine reads, failing open with a durable local buffer and later replay is usually more sensible than taking your product offline to protect a log.

Multi-tenant export, and the leak nobody tests for

Once you build customer-facing audit log export, you have created a new attack surface with a very high blast radius. The failure mode is a tenant filter applied in the query layer but not in the aggregation or the cached result, so tenant A's export contains three rows belonging to tenant B. That is a reportable privacy incident with a written notification obligation, and it is discovered by the customer rather than by you, because your customer is loading it into their SIEM and their detection engineer notices an unfamiliar domain.

Test it the way an attacker would. Write an automated test that seeds events for two tenants with overlapping timestamps and identifier prefixes and asserts on exact row counts, then make that test blocking in CI. Enforce the tenant predicate at the storage layer rather than in application code where a future refactor can drop it. Rate limit the export endpoint, because it is the cheapest bulk data exfiltration path in your product for a compromised customer credential. And log access to the audit log itself, which sounds recursive but is exactly what a security-literate buyer will ask about during their review of your customer-facing evidence surfaces.

Logs nobody reads are half a control

Retention satisfies a records requirement. It does not satisfy a monitoring requirement, and the two are separate criteria in SOC 2 and separate controls in ISO 27001. An auditor testing detection will ask what conditions generate an alert, who receives it, what the expected response time is, and will then ask you to produce three real alerts from the audit period along with evidence of what the responder did. A company with perfect nine month retention and zero alert rules fails that test cleanly.

You do not need a security operations centre to pass it. Five rules covering the things that matter get most teams there: administrative privilege granted outside the change process, impersonation of a customer account by staff, bulk export above a threshold, authentication from a country where you have no staff, and repeated authorisation failures against a single sensitive resource. Route them somewhere a human is accountable for, write down the triage expectation, and keep the tickets. The tickets are the evidence, and they are far more persuasive than the rule definitions. If you want that running continuously without hiring for it, that is the shape of a monitoring retainer.

When you should not build this

If you are pre-revenue with one product surface and no compliance commitment, building a bespoke audit pipeline is premature and you should not pay us or anyone else to design one. Your cloud provider and your identity provider already emit most of what an early audit will ask for. CloudTrail covers infrastructure actions, your identity provider's system log covers authentication and provisioning, your source control provider covers change history, and a database extension covers privileged data access. That combination has carried plenty of companies through a first SOC 2 Type II with no custom logging at all. Turn those on, set retention, and spend the engineering time on your product.

Custom application audit logging becomes worth building when your product has an in-app permission model your identity provider cannot see, when you handle PHI or cardholder data where access to individual records must be attributable, or when an enterprise buyer has contractually asked for exportable audit events. Absent one of those, the honest recommendation is to defer it. If HIPAA is the driver, the record access requirements shape the design, which we cover on HIPAA for digital health.

Retrofitting when you already shipped without it

Most teams reading this are not at day one, and the retrofit question is the real one. The mistake is trying to instrument every code path at once, which produces a six month project that stalls at forty percent and leaves you with partial coverage nobody can describe. Do it by control instead. Take the list of controls you need evidence for, work out the minimum set of events each one requires, and instrument only those. For a first SOC 2 that is typically authentication, authorisation changes, administrative actions, and production data exports. Four event families, not four hundred.

The second decision is what to say about the period before instrumentation existed. Do not backfill and do not imply coverage you did not have. A Type II covers a defined observation period, so the practical move is to instrument first, run for the minimum window your auditor accepts, and start the period after the logging is live. Telling an auditor that audit logging was implemented on a specific date and the period begins after it is a clean, normal conversation. Producing reconstructed events for a period when the pipeline did not exist is the kind of thing that turns a routine engagement into a scope expansion, and it is why we would rather move a client's start date than dress up a gap. Fixed-scope readiness work of this kind is priced on our pricing page so you can see what the instrumentation phase costs before you commit to a date with a customer.

Doing this for a deal? SOC 2 in 75 Days is our fixed-scope readiness track, with the price and the timeline published before you call us.

See SOC 2 in 75 DaysOr talk about a retainer

Before you go

Want the rest of this by email?

If this was useful, I send a few short notes on SOC 2 and compliance. Unsubscribe in one click, and replies reach me directly.

From Jacob Masse, principal of traztech. No spam, unsubscribe in one click.

Want a second opinion on where you stand?

We run SOC 2, ISO 27001 and the rest of the compliance stack for startups and SMEs, and the security testing that sits behind it. The first call is free, and we will tell you if you are not ready to start yet.

Book a free call

Track record

Who is actually doing the work

5
Published CVEs, including a CVSS 9.1
76
Controls taken from nothing to a passed SOC 2 Type II
Zero
Exceptions on that Type II report
20+
Penetration testing engagements delivered

Published vulnerability research

Five published CVEs. CVE-2024-45163 (CVSS 9.1) is a flaw in the Mirai botnet itself, which gave defenders a way to shut down attacker infrastructure. CVE-2026-42626 takes HP ENVY 5000 printers offline from any unauthenticated device on the same network.

A SOC 2 Type II built from nothing

At Humera, a venture-backed US security company, Jacob built the compliance programme in-house from nothing: no report, no policies, no documented controls. It ended in a Type II attestation across 76 controls with zero exceptions, on a team of 15.