Dev.to Security 🔐 Cybersecurity 👁 0 📖 2 min read

Your logs are leaking secrets in 24 languages

Your logs are leaking secrets in 24 languages Every leaked credential has the same origin story. Nobody commits password = "hunter2" to GitHub anymore — scanners catch that. What actually happens is quieter: someone wr

Your logs are leaking secrets in 24 languages

Every leaked credential has the same origin story. Nobody commits password = "hunter2" to GitHub anymore — scanners catch that. What actually happens is quieter: someone writes

  logger.info({ user }, 'login ok');

and user happens to carry a session token. The code review looked fine. The log aggregator, the error tracker, and three SaaS vendors now have a copy — forever.

## Field names are the wrong abstraction

The standard fix is a redaction path list: tell pino to hide req.headers.authorization, user.password, and so on. It works until:

  • the secret is in a field called note, because a support agent pasted an AWS key into a ticket;
  • your app logs text in Turkish, and the field is called şifrə, not password;
  • the token is inside a stack trace, not a field at all.

A blocklist of names can't see content. So I built flare-redact to read the values themselves.

## Rule 1: if you can't validate it, don't flag it

Content scanning has a reputation problem: false positives. Nobody keeps a tool that redacts random order IDs.

The fix is checksums. A 16-digit number is only a "credit card" if it passes Luhn. An IBAN has a mod-97 check. National IDs are stricter: India's Aadhaar uses the Verhoeff algorithm, France's NIR carries
official documentation examples.

Token formats do the same job for secrets: sk-ant-…, ghp_…, hvs.…, whsec_… are distinctive enough that false positives are effectively zero. Version 1.1.0 ships 75 detectors.

## Rule 2: hostile input is the normal case

A redactor runs on attacker-influenced text by definition — that's the whole point. Two consequences:

  1. ReDoS is a real threat. Every pattern uses bounded quantifiers; CI runs an adversarial benchmark that feeds pathological input and fails on quadratic blowups.
  2. Supply chain counts. The library has zero runtime dependencies. The SHA-256/HMAC primitives are implemented in-repo and verified against FIPS 180-4 and RFC 4231 test vectors. Releases are built by GitHub Actions and provenance-signed on npm.

## Rule 3: sometimes you need the secret back

The newest place secrets leak is LLM prompts. You can't just mask them — the model's reply references the values.

  import { wrapOpenAI } from 'flare-redact/llm';
  const openai = wrapOpenAI(new OpenAI());
  // prompts are redacted before leaving your process,
  // replies come back with the real values restored

Placeholders are opaque HMAC tokens, consistent per value, so "send it to the same email" still works. The mapping lives in a vault in your process — optionally persisted with AES-256-GCM.

## A war story about provenance

Mid-release, we transferred the repo to a GitHub organization. The next publish failed with E422: npm's provenance check refused the package because repository.url still pointed to the old owner, while
the signed build attestation said the new one.

Annoying? Slightly. But it's the system working: provenance means the npm page cryptographically proves which repo and workflow built the tarball. If your supply chain can't survive a rename, it can't
survive an attacker either. Turn it on for your packages: npm publish --provenance.

## Try it

  npm install flare-redact
  import { redact } from 'flare-redact';
  redact('User [email protected] paid with 4242 4242 4242 4242');
  // → 'User a***@*** paid with **** **** **** 4242'

It's MIT, TypeScript, Node 20+/browser/edge. If you can construct a false positive, open an issue — the detector suite grows by counter-example: https://github.com/flare-collection/flare-redact

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.