ScrutoraCode, cloud & consent
Back to Blog
·10 min readDeveloper Guide

How to Detect and Fix PHI Leaking in Application Logs

We found PHI leaking into logs in 4 out of 6 open-source healthcare systems we scanned. Here are the patterns to look for, the HIPAA risk they create, and how to fix them in your codebase.

By Scrutora · scrutora.com

Detecting PHI in Application Logs - Healthcare Developer Guide

The Problem Nobody Talks About

Every healthcare developer knows they need to encrypt data at rest and in transit. Most know they need access controls and session timeouts. But there's a HIPAA compliance gap hiding in plain sight: your application logs.

Application logs are the diary of your software. They record errors, track user actions, debug issues, and monitor performance. Developers rely on them every day. The problem is that in healthcare applications, those logs frequently contain Protected Health Information (PHI) that was never meant to be there.

When a developer writes console.log(patient) to debug a registration flow, they're serializing an entire patient object into a log file. That log file is typically stored in plain text, retained for weeks or months, accessible to operations staff, and often shipped to third-party log aggregation services like Datadog, Splunk, or CloudWatch.

The HIPAA risk: Under 45 CFR § 164.530(j), covered entities must implement policies to ensure PHI is not improperly disclosed through operational systems. Application logs that contain patient names, medical record numbers, diagnoses, or medication details may create risk under this requirement. If those logs are accessible to unauthorized personnel or stored without encryption, the exposure surface grows significantly.

Real Examples from Open-Source Healthcare Systems

We recently scanned 6 popular open-source healthcare applications using AST-based static analysis. PHI in logs was one of the most common findings, appearing in 4 out of 6 projects. Here are the patterns we found.

For the full analysis of all 79 findings across 6 projects, see our complete HIPAA security analysis.

Pattern 1: Serializing Patient Objects with toString()

Found in: OpenMRS Core (Java), Danphe EMR (C#)

This is the most common pattern. A developer logs a patient or medical object using toString(), which serializes every field on that object into the log output. If the object contains name, date of birth, diagnosis, or any other PHI field, it all ends up in the log.

JavaSerializing patient object in log
// BAD: toString() dumps every field including PHI
logger.info("Processing patient: " + patient.toString());
// Log output: "Processing patient: Patient{name=John Smith, dob=1985-03-12, mrn=MRN-12345, diagnosis=Type 2 Diabetes}"

// BAD: String concatenation with object reference
logger.debug("Admission record: " + admissionRecord);
// Java implicitly calls toString() — same problem

Pattern 2: JSON.stringify() in JavaScript/Node.js

Found in: SudoEMR (JavaScript/React), HospitalRun Frontend (JavaScript/Ember)

In JavaScript applications, JSON.stringify() is the equivalent of toString(). Developers use it to quickly dump objects into log messages or error handlers. When the object is a patient record, the entire payload ends up in the log.

JavaScriptJSON.stringify dumps all patient fields
// BAD: Serializes entire patient object
console.log("Patient data:", JSON.stringify(patient));
// Output: {"id":"P-001","name":"Jane Doe","ssn":"123-45-6789","medications":["Metformin","Lisinopril"]}

// BAD: Template literal with object
console.log(`Creating appointment for ${JSON.stringify(patientRecord)}`);

Pattern 3: PHI in Error Messages and Exception Handlers

Found in: Danphe EMR (C#), OpenMRS Core (Java)

Error handlers are a particularly sneaky source of PHI in logs. When an operation fails, developers often include the object that caused the failure in the error message. This means PHI only shows up in logs when something goes wrong, making it hard to catch during normal testing.

C#PHI in catch blocks
// BAD: Exception handler logs the patient object
try {
    await SavePatientAsync(patient);
} catch (Exception ex) {
    _logger.LogError(ex, "Failed to save patient: {Patient}", patient);
    // Structured logging serializes the entire patient object
}

// BAD: Including data in error context
catch (DbUpdateException ex) {
    _logger.LogError("DB error for record: " + JsonConvert.SerializeObject(medicalRecord));
}

Pattern 4: Logging PHI Object Properties Directly

Found in: Danphe EMR (C#), OpenMRS Core (Java)

Sometimes developers don't serialize the entire object but still log specific PHI fields. This is harder to detect because the log statement looks intentional and targeted.

JavaLogging individual PHI fields
// BAD: Logging specific PHI fields
logger.info("Scheduling appointment for " + patient.getName() + " DOB: " + patient.getDateOfBirth());

// BAD: Logging medical data
logger.debug("Prescription: " + prescription.getMedicationName() + " for " + patient.getMrn());

How to Find PHI in Your Logs

You can start looking for these patterns in your codebase right now. Here are grep commands that catch the most common violations:

Search for Object Serialization in Logs

BashGrep for common PHI-in-log patterns
# Java — toString() in log statements
grep -rn "log.*\.toString()" --include="*.java" src/

# JavaScript — JSON.stringify in console/logger calls
grep -rn "console\.log.*JSON\.stringify\|logger.*JSON\.stringify" --include="*.js" --include="*.ts" src/

# C# — SerializeObject in logging
grep -rn "Log.*SerializeObject\|Log.*ToString()" --include="*.cs" src/

# Python — str() or repr() on objects in logging
grep -rn "logging.*str(\|logging.*repr(" --include="*.py" src/

# All languages — PHI keywords in log statements
grep -rn "log.*patient\|log.*diagnosis\|log.*medication\|log.*ssn\|log.*mrn" -i src/

PHI Keywords to Search For

Search your log statements for these keywords. If any appear in a logging call alongside a variable or object reference, you likely have PHI in your logs:

  • Patient identifiers: patient, member, subscriber, beneficiary, enrollee
  • Medical data: diagnosis, medication, prescription, allergy, procedure, treatment, lab, vitals
  • Personal info: ssn, dateOfBirth, dob, address, phone, email, insurance, mrn (medical record number)
  • Financial: billing, claim, copay, deductible, payment

Check Your Log Aggregation Service

If you use Datadog, Splunk, CloudWatch, or any log aggregation service, search there too. Run queries for the same PHI keywords. If you find patient names or medical data in your log aggregation platform, that data has already been transmitted to and stored by a third party. Check whether that service is covered by a BAA.

How to Fix PHI in Logs

Fix 1: Create Safe Logging Methods

Instead of logging raw objects, create a safe representation that only includes non-PHI fields:

JavaSafe logging with toLogString()
// Override toString() on PHI models to be log-safe
public class Patient {
    // ... fields ...

    @Override
    public String toString() {
        // Only return non-PHI fields
        return "Patient{id=" + id + ", status=" + status + "}";
    }

    // Full representation only for authorized contexts
    public String toAuditString() {
        return "Patient{id=" + id + ", name=" + name + ", mrn=" + mrn + "}";
    }
}
JavaScriptSafe logging helper
// Create a safe-to-log version of patient objects
function toLogSafe(patient) {
  return {
    id: patient.id,
    status: patient.status,
    createdAt: patient.createdAt,
    // Explicitly exclude: name, dob, ssn, medications, etc.
  };
}

// Usage
console.log("Processing patient:", toLogSafe(patient));

Fix 2: Use Log Redaction Libraries

Several libraries can automatically redact sensitive fields from log output:

JavaScriptfast-redact for Node.js
const fastRedact = require('fast-redact');
const redact = fastRedact({
  paths: ['name', 'ssn', 'dob', 'email', 'phone', 'address',
          'medications.*', 'diagnosis', 'mrn', 'insurance'],
  serialize: JSON.stringify,
});

// Automatically redacts PHI fields
logger.info(redact(patientRecord));
// Output: {"id":"P-001","name":"[REDACTED]","ssn":"[REDACTED]","status":"active"}
Pythonscrubadub for Python
import scrubadub
import logging

# Scrub PII from log messages before writing
class ScrubHandler(logging.Handler):
    def emit(self, record):
        record.msg = scrubadub.clean(str(record.msg))
        # Forward to actual handler

logger.addHandler(ScrubHandler())

Fix 3: Use Structured Logging with Field Allowlists

Instead of logging free-text messages with interpolated objects, use structured logging that explicitly defines which fields are safe to log:

JavaStructured logging with explicit fields
// BAD: Free-text with object interpolation
logger.info("Processing patient: " + patient);

// GOOD: Structured logging with safe fields only
logger.info("Processing patient",
    kv("patientId", patient.getId()),
    kv("status", patient.getStatus()),
    kv("action", "registration"));

Fix 4: Strip PHI from Error Messages

For exception handlers, never include data objects in error messages. Use error codes and correlation IDs instead:

C#Safe error logging
// BAD: Including patient data in error
catch (Exception ex) {
    _logger.LogError(ex, "Failed to save: {Patient}", patient);
}

// GOOD: Use correlation ID, not PHI
catch (Exception ex) {
    _logger.LogError(ex, "Failed to save patient. CorrelationId={CorrelationId}, PatientId={PatientId}",
        correlationId, patient.Id);
}

PHI-Safe Logging Checklist

Use this checklist to audit your healthcare application's logging:

  1. 1Search your codebase for console.log, logger.info, logger.error, logger.debug, and print statements that reference patient, medical, health, diagnosis, or medication objects.
  2. 2Check every toString(), ToString(), and JSON.stringify() call in logging contexts. If it serializes a healthcare data model, it's likely logging PHI.
  3. 3Review all catch/exception blocks. Error handlers that include data objects in their messages are a common source of PHI leaks.
  4. 4Override toString() and __repr__() on all PHI data models to only return non-sensitive fields (ID, status, timestamps).
  5. 5Implement a log redaction library (fast-redact for JS, scrubadub for Python) as a safety net.
  6. 6Use structured logging with explicit field allowlists instead of free-text message interpolation.
  7. 7Audit your log aggregation service (Datadog, Splunk, CloudWatch) for existing PHI. Search for patient names and medical terms.
  8. 8Ensure any third-party log service that might receive PHI is covered by a Business Associate Agreement (BAA).
  9. 9Set up a pre-commit hook or CI check that flags new log statements containing PHI keywords.
  10. 10Review logging practices during code review. Make "no PHI in logs" a standard checklist item for every pull request.

The HIPAA Requirement

HIPAA's Security Rule at 45 CFR § 164.312 requires technical safeguards to protect electronic PHI. While the Security Rule doesn't specifically mention application logs, several provisions apply directly:

  • Access Controls §164.312(a)(1): If your logs contain PHI, then access to those logs must be restricted to authorized personnel only. Operations staff and third-party log services may not be authorized to view PHI.
  • Audit Controls §164.312(b): If PHI is in your logs, you need to track who accesses those logs. This creates a recursive compliance problem: you need audit logs for your audit logs.
  • Transmission Security §164.312(e)(1): If logs containing PHI are transmitted to a third-party service (Datadog, Splunk, etc.), that transmission must be encrypted.

The simplest way to satisfy all of these requirements is to never put PHI in logs in the first place. Prevention is far easier than retroactive access control on log files.

Disclaimer

This article is based on patterns identified through automated static code analysis of publicly available open-source healthcare projects. Static analysis identifies code patterns that may indicate risks. It cannot determine runtime behavior or whether compensating controls exist at the infrastructure level (such as log redaction pipelines).

The code examples shown are real patterns from open-source projects, used for educational purposes. The presence of these patterns does not necessarily mean PHI was actually exposed in production environments.

We have deep respect for the teams building these open-source healthcare tools. They're doing important work, often with limited resources. The purpose of this article is to help all healthcare developers recognize and fix a common compliance gap.

Find PHI in Your Logs Automatically

Searching your codebase manually with grep works, but it doesn't scale. Every new pull request, every new developer can introduce PHI logging patterns that manual review misses.

Our scanner detects PHI in logs (LOG-001) along with 12 other HIPAA Security Rule safeguards. Because it parses your code into an abstract syntax tree, it catches toString() serialization, JSON.stringify calls, and PHI in error handlers that simple grep commands miss.