ASVS 5.0 V16.2.5 requires that when sensitive data might be logged, the application enforces rules based on its protection level. Some data, such as passwords and payment card numbers, must never be logged. Other data, such as session tokens, may be logged only hashed or masked. It is Level 2. Classify the data, then redact in code and test that nothing leaks.
Why it matters
Logs are copied widely, kept for months and read by many people. A password in a log is a credential leak that no access control on the application can undo. Real incidents have come from request body logging, verbose debug modes and exception messages that include connection strings.
How to implement it
First write a short classification table:
- Never log: passwords, private keys, full card numbers, card security codes, API secrets.
- Log masked or hashed: session ids, access tokens, email addresses, phone numbers.
- Log freely: opaque internal ids, event names, status codes.
Then enforce it with a redaction layer at the logger, so it does not depend on each developer remembering:
import logging, re, hashlib
DENY_KEYS = {"password", "passwd", "secret", "card_number", "cvv", "private_key"}
HASH_KEYS = {"session_id", "access_token", "refresh_token"}
def scrub(fields: dict) -> dict:
out = {}
for k, v in fields.items():
lk = k.lower()
if lk in DENY_KEYS:
out[k] = "[REDACTED]"
elif lk in HASH_KEYS:
out[k] = hashlib.sha256(str(v).encode()).hexdigest()[:12]
else:
out[k] = v
return out
class Scrubber(logging.Filter):
def filter(self, record):
if isinstance(getattr(record, "fields", None), dict):
record.fields = scrub(record.fields)
return True
logging.getLogger().addFilter(Scrubber())
Pair this with pattern scanning for values that look like card numbers or keys in free text messages. Do not log whole request or response bodies by default. Turn off debug body logging in production.
Catch leaks before they ship with a secrets scan in CI, which finds credentials committed in code and test fixtures. The demo below shows the idea.
Click through it at your own pace, or open it full screen.
How to test it
- Run the main flows in a test environment with known canary values: a password such as
Canary-Pw-91827, a fake card number from a test range and a recognizable token. - Search all log destinations for each canary. Any hit is a failure.
- Force errors, such as a failed database connection and a malformed request, and check exception output.
- Enable the highest log level used in production and repeat.
- Keep the canary search as an automated test that runs on every release.
Common mistakes
- Redacting by key name only, so the same value logged under a different key escapes.
- Logging the full URL, which may hold tokens in the query string.
- Hashing low entropy values such as phone numbers without a secret, which can be reversed by guessing.
- Forgetting that third party libraries log request details on their own.
Quick check
Can I log a hashed password?
Do not. Credentials fall in the never log category, and hashes can still be attacked offline.
Which level is this?
Level 2.