A health app should never write secrets, session identifiers, full request bodies, free text typed by patients or clinicians, or direct patient identifiers to its logs. Log event types, internal opaque IDs, outcomes and timings instead. Logs are copied, shipped and searched by many people, so anything sensitive in them spreads.
Why logs are the usual leak
Teams protect the database carefully and then print the same data into a log line while debugging. Logs get replicated to a search service, a third party monitoring tool, a backup and a laptop. Each copy has its own access list. The data in them is rarely covered by the same controls as the primary record.
The OWASP Logging Cheat Sheet is direct about this. It says to remove, mask, sanitize, hash or encrypt items such as session identifiers, access tokens, passwords, connection strings and encryption keys. It also lists health data among sensitive personal data to treat the same way, and says to take special care with names, phone numbers and email addresses.
The never-log list
| Category | Examples | Log this instead |
|---|---|---|
| Secrets | API keys, passwords, tokens, connection strings | Which credential ID was used, never the value |
| Session material | Session IDs, cookies, auth headers | A hashed or truncated session reference |
| Direct identifiers | Names, phone, email, record numbers, addresses | An internal opaque user or record ID |
| Clinical content | Diagnoses, notes, messages, lab values | Event type and resource type only |
| Free text | Search boxes, chat, form notes | Length and validation result |
| Full payloads | Whole request or response bodies | Selected, allowlisted fields |
The opaque ID matters. If a support engineer can look the ID up in a controlled system, the log stays useful while the log itself reveals nothing about the person.
Steps to put this in place
- Write the never-log list down and put it in the contributing guide.
- Use an allowlist logger: a function that accepts named fields, not a raw object dump.
- Turn off framework features that print whole requests, such as verbose HTTP client logging, in production.
- Add a redaction layer in the logging pipeline as a backstop, not as the only control.
- Write a test that logs a sample record and asserts the output has no identifier fields.
- Review a sample of real production logs each quarter and record what you found.
Step 2 is the one that holds up. Redaction by pattern misses things, because a patient can type anything into a free text box.
What the Security Rule text adds
The HIPAA technical safeguards in 45 CFR 164.312 include an audit controls standard: mechanisms that record and review activity in systems that contain electronic PHI. That is a reason to keep good logs, not a reason to log content. An audit trail records who did what to which record and when. It does not need the record contents. This is general information, not legal advice, and your counsel should confirm what applies to you.
Where Safeguard fits
Safeguard scans your dependencies and code for known vulnerabilities and can enforce policy gates in pull requests. That helps with the library side of logging risk, such as a logging package with a known flaw. The never-log decisions themselves stay with your engineering and privacy owners. You can run a free scan at gold.safeguard.sh/scan.
Quick check
Is a patient ID in a log line a problem?
An internal opaque ID that only resolves inside a controlled system is far safer than a name or record number. If the ID is itself a medical record number or similar, treat it as a direct identifier.
Can redaction replace the allowlist?
No. Pattern redaction is a backstop. Free text can contain anything, so the safer rule is to never log it.
Sources
Last reviewed: 19 October 2026.