What it is#
Sensitive data includes credentials, session tokens, cryptographic keys, payment information, personal data, health information, confidential business records, and information whose combination creates risk.
Exposure can occur in API responses, client-side code, URLs, logs, analytics, error messages, backups, object storage, caches, message queues, temporary files, monitoring systems, and network traffic. Encryption is one control, but exposure also results from returning too much data or authorizing the wrong audience.
The first question is whether the data needs to exist. Data that is never collected, copied, or retained cannot be leaked from that location.
Why it matters#
- Account takeover through exposed passwords, reset tokens, API keys, or session identifiers.
- Identity theft, privacy harm, fraud, and regulatory notification obligations.
- Compromise of downstream systems using leaked service credentials or signing keys.
- Cross-tenant disclosure through overbroad APIs, exports, caches, or backups.
- Persistent exposure because logs and backups often have long retention and broad access.
- Loss of trust and contractual consequences even when no further exploitation occurs.
Low for limited non-sensitive technical details. High to critical for credentials, private keys, regulated data, bulk records, cross-tenant exposure, or information enabling further compromise. Actual severity depends on context: who is affected, what data or actions are reachable, and existing controls.
Examples#
// Vulnerable: records the entire request, including secrets and tokens.
app.post('/login', async (req, res) => {
logger.info({ body: req.body, authorization: req.headers.authorization });
const session = await authenticate(req.body.email, req.body.password);
res.json(session);
});
// Fixed: log an event with an explicit non-sensitive field allowlist.
app.post('/login', async (req, res) => {
const result = await authenticate(req.body.email, req.body.password);
logger.info({
event: 'authentication_attempt',
accountId: result.accountId,
outcome: result.outcome,
requestId: req.id,
});
res.status(result.outcome === 'success' ? 204 : 401).end();
});
Response DTOs should be similarly explicit. Do not serialize complete database or identity-provider objects directly to clients.
How to fix it#
1. Classify and minimize data. Document data elements, purpose, owner, locations, recipients, and retention. Stop collecting fields that are not needed.
2. Return explicit response models. Allowlist fields for each audience. Enforce object and tenant authorization before reading or exporting data.
3. Keep secrets out of URLs and client code. Query strings leak through history, logs, analytics, and referrers. Browser-delivered JavaScript cannot safely contain a server secret.
4. Encrypt data in transit and at rest. Use current TLS and managed storage encryption. Apply field-level or envelope encryption when database or backup operators should not see especially sensitive values.
5. Manage keys separately. Keep encryption keys in a managed key service or hardware-backed system, restrict use by identity and purpose, rotate them, and test recovery.
6. Hash passwords correctly. Use a dedicated password-hashing function with unique salts and tuned work factors. Passwords should not be decryptable.
7. Design safe telemetry. Use structured, allowlisted logging. Exclude credentials, tokens, cookies, full payment data, recovery answers, and sensitive request bodies from logs, traces, crash reports, and analytics.
8. Restrict storage and backups. Apply least privilege, tenant isolation, retention, deletion, encryption, and monitored restore procedures to primary data, replicas, exports, caches, and backups.
9. Set cache and download policy. Prevent shared caching of personalized responses. Use authenticated download handlers, safe content headers, short-lived links, and expiration.
10. Prepare for exposure. Maintain secret-rotation, session-revocation, notification, and incident-response procedures appropriate to each data class.
How to test for it#
- Build a data-flow inventory from collection through APIs, queues, storage, logs, analytics, backups, exports, and deletion.
- Use synthetic marked data in test accounts, then search authorized logs and storage systems for unexpected copies.
- Compare API and export responses across roles and tenants to confirm only required fields are returned.
- Inspect URLs, browser storage, page source, JavaScript bundles, cache headers, referrers, and error responses for secrets or personal data.
- Review logging and tracing pipelines, including third-party observability platforms, using synthetic credentials rather than real secrets.
- Verify encryption configuration, key permissions, rotation, backup protection, and restore behavior.
- Confirm retention and deletion jobs affect replicas, caches, exports, and backups according to policy.
- Add automated schema tests that fail when sensitive database fields appear in public response models.
References#
- CWE-200: Exposure of Sensitive Information to an Unauthorized Actor ↗ (opens in a new tab)cwe.mitre.org
- CWE-312: Cleartext Storage of Sensitive Information ↗ (opens in a new tab)cwe.mitre.org
- OWASP Top 10 A02:2021 Cryptographic Failures ↗ (opens in a new tab)owasp.org
- OWASP Cryptographic Storage Cheat Sheet ↗ (opens in a new tab)cheatsheetseries.owasp.org
- OWASP Logging Cheat Sheet ↗ (opens in a new tab)cheatsheetseries.owasp.org
- OWASP Secrets Management Cheat Sheet ↗ (opens in a new tab)cheatsheetseries.owasp.org