Security guides
Security guide

Secrets in Logs: How to Find Them, Respond, and Stop It Happening

By Sazabi Team

Last verified on Nov 8, 2026.

Secrets and personal data reach logs by accident: a debug line prints a request, an exception carries a connection string, a URL holds a token. The fix has an order. Rotate the credential first, find where else it was copied, purge and restrict access second, and then stop the leak at the logger so it does not recur.

How Secrets End Up in Logs

The same few paths account for most leaks.

  • Headers. Request logging that dumps all headers records Authorization and Cookie values.
  • URLs with tokens. Query strings such as ?access_token=... land in access logs at the load balancer, the proxy, and the app, because every layer logs the request line.
  • Debug dumps. logger.debug(request) or printing a whole config object writes every field, including the ones that hold credentials.
  • Exceptions. Stack traces and error messages include the failing connection string, the SQL with bound values, or the response body from a third-party API.
  • Build and deploy output. Environment dumps in CI logs expose variables that were never meant to be printed.
  • User input. Free-text fields and form values contain emails, phone numbers, and other personal data that you never decided to collect in logs.

OWASP's Logging Cheat Sheet lists what should not be recorded directly and should instead be removed, masked, sanitized, hashed, or encrypted: session identification values, access tokens, authentication passwords, database connection strings, encryption keys and other primary secrets, bank account or payment card data, and sensitive personal data.

Find Secrets in Logs with Regexes

Most credentials have a recognizable prefix. These are documented by the issuers.

SecretPrefix or shapeSource
AWS access key IDAKIA followed by 16 characters (ASIA for temporary keys)AWS IAM identifiers
GitHub tokensghp_, github_pat_, gho_, ghs_GitHub authentication docs
Stripe live keyssk_live_, rk_live_Stripe API keys
JWTThree base64url parts separated by dots, header starting eyJRFC 7519

Run these against exported logs. Each prints the file, line number, and the match:

grep -rnoE '\b(AKIA|ASIA)[0-9A-Z]{16}\b' /var/log/app/
grep -rnoE '\b(ghp|gho|ghs)_[A-Za-z0-9]{36}\b|github_pat_[A-Za-z0-9_]{22,}' /var/log/app/
grep -rnoE '\b[sr]k_live_[A-Za-z0-9]{10,}' /var/log/app/
grep -rnoE 'eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+' /var/log/app/

Then look for secrets that have no prefix. Query-string parameters are the first place to check, and email addresses are the usual personal data:

grep -rnoiE '[?&](access_token|token|api_key|apikey|key|password|secret)=[^& ]+' /var/log/app/
grep -rnoE '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}' /var/log/app/ | wc -l

Treat regex matches as leads. Prefixes can change, and a match does not tell you whether the credential is live. Hits on ghp_ or AKIA are strong enough to act on immediately. If the leak is on the public internet, also read exposure for how exposed secrets are found by others.

Respond: Rotate First, Then Purge

  1. Rotate or revoke the credential. Assume that anyone with log access, including log vendors, contractors, and anyone with a stale export, has seen it. For AWS keys follow leaked AWS access key. For other providers see leaked API key.
  2. Check for use. Look at the provider's audit log for activity from the key between the first leaked line and the rotation time.
  3. Fix the source. Find the log statement or config that wrote it and change it before the next deploy, or the same secret will be logged again in the new key.
  4. Purge and restrict. Delete or rewrite the affected records in the logging system, and in backups, exports, and downstream indexes. OWASP notes that log data must not be kept beyond the required retention period and must not be destroyed before it, so check legal and contractual obligations before deleting broadly. See log retention.
  5. Tighten who can read logs. If every engineer can read production logs, a secret in a log is a secret shared with all of them.

For personal data, involve whoever owns privacy obligations at your company. Rotation does not fix an exposed email address.

Prevent: Redact at the Logger

The earliest place to stop a secret is the process writing the log line.

pino. The redact option takes paths to keys that hold sensitive data. It supports wildcards, a custom censor, and remove: true:

const pino = require('pino')

const logger = pino({
  redact: {
    paths: ['req.headers.authorization', 'req.headers.cookie', '*.password', 'user.email'],
    censor: '***',
  },
})

Pino's docs warn that path strings must not come from user input. See the pino logger guide for setup.

Python. A logging filter can modify the record in place before it is emitted. This one masks known prefixes in the formatted message:

import logging, re

PATTERNS = [
    re.compile(r"\b(?:AKIA|ASIA)[0-9A-Z]{16}\b"),
    re.compile(r"\b(?:ghp|gho|ghs)_[A-Za-z0-9]{36}\b"),
    re.compile(r"\b[sr]k_live_[A-Za-z0-9]+"),
    re.compile(r"eyJ[\w-]+\.[\w-]+\.[\w-]+"),
]

class RedactSecrets(logging.Filter):
    def filter(self, record):
        message = record.getMessage()
        for pattern in PATTERNS:
            message = pattern.sub("***", message)
        record.msg, record.args = message, None
        return True

handler = logging.StreamHandler()
handler.addFilter(RedactSecrets())

Attach the filter to handlers, not only loggers, so it applies to records from every child logger. More on setup in Python logging.

OpenTelemetry Collector. If logs pass through a Collector, the redaction processor deletes attributes not on an allowed list and masks attribute values that match blocked regular expressions:

processors:
  redaction:
    allow_all_keys: true
    blocked_key_patterns:
      - '.*token.*'
      - '.*api_key.*'
    blocked_values:
      - 'AKIA[0-9A-Z]{16}'
      - 'ghp_[A-Za-z0-9]{36}'

It works on attributes. The transform processor's replace_pattern function applies a regular expression to a string field when you need to rewrite one.

Pipeline redaction is a safety net, not a substitute for not logging the value. The general rule in logging best practices applies: log identifiers, not credentials.

Redacting in Sazabi

Sazabi, an AI-native observability and security platform, offers a Redact sensitive data setting in project settings. When on, it replaces emails, phone numbers, US Social Security numbers, card numbers, and secrets such as cloud access keys, API keys, JWTs, and private keys with placeholders before logs are stored. It is off by default, applies only to logs received after you turn it on, and uses fixed patterns, so it does not catch names or other free-form personal data. Sazabi's production checklist says to redact at your own log source or collector too, where you own the data.

Frequently Asked Questions

What should I do first when I find a secret in my logs?

Rotate or revoke the credential first. Anyone who could read the logs may already have it, so deleting the log line does not make it safe. Purge and tighten access after the credential is dead.

How do I search logs for API keys and tokens?

Match known prefixes with regular expressions: AKIA for AWS access key IDs, ghp_ and github_pat_ for GitHub tokens, sk_live_ for Stripe secret keys, and three dot-separated base64url parts starting with eyJ for JWTs. Also search URL query strings for parameters named token, key, or password.

Is it enough to redact at the log platform instead of in the app?

It helps but is the second line of defense. Redaction at the logger or collector stops the value leaving your systems at all. Platform-side redaction only covers logs received after you turn it on and uses fixed patterns.

Does deleting logs remove the exposure?

Not by itself. Copies may exist in backups, archives, exports, and downstream tools. Deleting is part of cleanup, after rotation, and must be weighed against any required retention period.

Sources

  1. OWASP lists access tokens, session identifiers, passwords, connection strings, encryption keys, and payment card data as data that should be removed, masked, hashed, or encrypted instead of logged; log data must not be kept beyond the retention period (accessed Nov 8, 2026)
  2. GitHub token prefixes: ghp_ for classic personal access tokens, github_pat_ for fine-grained tokens, gho_ for OAuth tokens, ghs_ for GitHub App installation tokens (accessed Nov 8, 2026)
  3. IAM unique ID prefix AKIA marks an access key and ASIA marks a temporary STS access key ID (accessed Nov 8, 2026)
  4. Stripe live mode keys start with pk_live_, rk_live_, and sk_live_ (accessed Nov 8, 2026)
  5. A JWT is a sequence of base64url-encoded parts separated by period characters, and the encoded JOSE header in the RFC example begins eyJ (accessed Nov 8, 2026)
  6. pino redacts keys at the paths in the redact option, supports wildcard paths, a custom censor, and remove: true (accessed Nov 8, 2026)
  7. Python logging filters can modify log records in place before they are emitted (accessed Nov 8, 2026)
  8. The OpenTelemetry Collector redaction processor deletes attributes that are not on an allowed list and masks attribute values that match blocked value regular expressions (accessed Nov 8, 2026)
  9. The transform processor can rewrite telemetry fields, with replace_pattern applying a regular expression and replacement to a string field (accessed Nov 8, 2026)
  10. Sazabi can redact emails, phone numbers, SSNs, card numbers, and secrets at intake when enabled, applies only to new logs, and uses fixed patterns that do not catch free-form personal data (accessed Nov 8, 2026)
  11. Sazabi advises redacting secrets and sensitive fields at your log source or collector, the point where you own the data (accessed Nov 8, 2026)