Safeguard
Best Practices

Data loss prevention for developers: stopping leaks before they hit the network

CWE-532 covers secrets logged in plaintext — the exact bug that led Twitter to reset every password on May 3, 2018. Network DLP can't catch it; your code has to.

Safeguard Research Team
Research
7 min read

On May 3, 2018, Twitter told all 330 million of its users to reset their passwords after discovering that a bug had been writing plaintext passwords to an internal log before the hashing step ever completed. No breach was confirmed, and Twitter never disclosed how many logs actually contained the data — but the mechanism is the textbook case of what MITRE formally classifies as CWE-532, "Insertion of Sensitive Information into Log File," now in its 4.20 revision at cwe.mitre.org. The bug wasn't a network intrusion. It was a logging statement that ran before a security control, on infrastructure the company already trusted. That distinction matters because most data loss prevention budgets still point outward — at egress filters, proxy rules, and network DLP appliances scanning traffic as it leaves the building. None of that helps when the sensitive data was already written to a log file, a stack trace, or a browser's localStorage bucket before it ever touched the wire. This post is a developer-facing look at the leak paths network DLP structurally can't see, and the concrete practices — structured redaction, scrubbed error responses, disciplined client-side storage, and pre-push secret scanning — that catch sensitive data at the point it's created, not the point it's exfiltrated.

Why doesn't network DLP catch most sensitive data leaks?

Network DLP inspects data as it crosses a boundary — egress to the internet, an email attachment, a USB transfer — which means it only works on data already in motion toward somewhere the tool can watch. It has no visibility into a log file sitting on a server's local disk, a Sentry event captured client-side before any network call, or a value written to localStorage in a user's browser. MITRE's own mitigation guidance for CWE-532 doesn't mention network controls at all: it recommends never writing secrets to logs in the first place, stripping debug-level logging before production deploys, restricting log file permissions, and using static analysis to catch violations before merge. That's the core asymmetry — DLP appliances are built to police a perimeter, but a huge share of real sensitive-data exposure (leaked credentials in a repo, PII in a crash report, a session token cached client-side) never crosses that perimeter in a form the appliance would recognize as sensitive. The leak already happened at write time.

How do secrets end up in logs in the first place?

The Twitter incident is instructive precisely because it wasn't careless logging of an obviously sensitive field — it was an ordering bug. A password went into an internal log before the bcrypt hashing step that was supposed to protect it, meaning the logging code itself may have been "correct" by the standards of whoever wrote it, while the sequence around it was wrong. This is common: a debug log added to troubleshoot an auth flow captures the full request body, including a password or API key, and never gets removed. A third-party SDK error handler logs the entire exception object, which happens to include a stack frame with a database connection string in its arguments. The fix isn't "never log" — it's structured logging with field-level allowlisting, where a logger only emits fields explicitly marked safe, rather than blanket object serialization where every new field is safe by default. GitHub's secret-scanning partner program exists precisely because this pattern is common enough at scale that providers now proactively scan for and revoke credentials that leak this way.

What's the right way to handle error messages and stack traces?

A stack trace or a verbose 500 response is a convenience for the developer who wrote the code and a reconnaissance gift for anyone else who sees it. Unscrubbed error responses routinely leak internal file paths, ORM query fragments with table and column names, framework version strings, and — in the worst cases — the actual PII or credentials involved in the failed request. The practice that holds up: catch exceptions at the boundary of your application (the outermost middleware or handler) and return a generic, logged-server-side-only error to the client, with the full detail going to your internal logging pipeline instead of the HTTP response body. This is a deliberate trade of developer convenience for exposure surface — verbose errors are genuinely useful in a local dev environment, which is exactly why the split between dev and production error-handling configuration has to be enforced by the framework's config, not by developer memory.

Why is client-side storage a data loss risk network tools never see?

Anything written to localStorage or sessionStorage in a browser is readable by any JavaScript running on that page — including an attacker's payload delivered through an XSS vulnerability elsewhere in the app, because both storage APIs are plain synchronous JavaScript objects with no access restriction beyond same-origin. This is the standard argument for why session tokens belong in httpOnly cookies rather than localStorage: an httpOnly cookie is invisible to JavaScript entirely, so an XSS payload can't read it even if it can execute arbitrary code on the page. The same risk extends to client-side analytics and error-monitoring SDKs — tools like Sentry and Datadog RUM capture full request and response payloads, form field values, and breadcrumb trails by default, which means an unconfigured integration can quietly ship user PII to a third-party vendor's servers on every page load. Both tools support a beforeSend scrubbing hook that strips or redacts fields before the event ever leaves the browser, but it only works if a developer explicitly wires it in — the default behavior is to capture everything.

Are CI/CD build logs a real leak surface?

Yes, and they're an easy one to overlook because they feel internal. A curl -v call in a deploy script echoes full request headers, including Authorization: Bearer tokens, straight into a build log that may be retained for months and readable by every contributor with CI access. Environment variable dumps for debugging — a stray printenv or a shell trace flag left in a pipeline — do the same for every secret injected into that job. CWE-532's own mitigation list explicitly calls out log file permissions as a control surface, and CI logs are frequently the most permissively-shared logs in an organization, since access is often tied to repository membership rather than a tighter secrets-access policy. The fix is boring but effective: mask known secret patterns in CI log output at the platform level (most CI providers support this natively for declared secrets), avoid verbose flags in scripts that touch credentialed requests, and treat build log retention settings as a security control, not an operational afterthought.

How does Safeguard help developers prevent these leaks before they happen?

Safeguard's secrets scanning is built for exactly this "catch it before it's a network problem" model. It scans source code, Git history, container image layers, packaged artifacts, and CI build logs for issuer-specific patterns across 200-plus providers, generic patterns like private keys and JWTs, and high-entropy strings that don't match a known format — then verifies each finding live against the issuing service (an AWS key against sts:GetCallerIdentity, a Stripe key against /v1/balance) so a verified, exploitable secret is distinguished from a false positive automatically. A pre-push git hook (safeguard install-hook --hook pre-push) blocks a secret from ever leaving a developer's machine, and for anything that does slip through, the remediation playbook can revoke the credential at the issuer, open a rotation PR, and optionally purge it from Git history — closing the loop from "written into a log or a commit" to "revoked," entirely upstream of anything a network DLP appliance would ever get a chance to inspect.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.