Redact sume_live_ API keys from Python logs with a handler filter

A logging.Filter on a logger skips records from child loggers. Put the redaction filter on the handler so a sume_live_ key never reaches the log file.

4 min readSume
All posts

Sume API keys start with sume_live_. The authentication docs say keys belong on trusted servers and that a key which shows in logs or chat history should be rotated. A redaction filter is a seatbelt, not a cure: it stops the next leak, and it does not make an already logged key safe.

In Python the usual mistake is attaching the filter to a logger. A filter on a logger runs only for records logged directly on that logger, not for records from its child loggers that propagate up. Attach it to the handler and every record that reaches the handler is cleaned.

The filter

It formats the message first, so keys inside %s arguments are caught, then clears args so the message is not formatted twice.

import logging, re

KEY = re.compile(r"sume_live_[\w-]+")

class RedactSumeKeys(logging.Filter):
    def filter(self, record):
        record.msg = KEY.sub("sume_live_[redacted]", record.getMessage())
        record.args = ()
        return True

handler = logging.StreamHandler()
handler.addFilter(RedactSumeKeys())  # on the handler, not a logger
logging.basicConfig(level=logging.INFO, handlers=[handler])

http_log = logging.getLogger("myapp.http.sume")  # a child logger still passes the handler
http_log.info("POST /v1/images headers=%s", {"x-api-key": "sume_live_abc123-XYZ"})
http_log.info("rotated to sume_live_new_key_789")

What the output looks like

Both log lines print sume_live_[redacted], including the one from myapp.http.sume, a child logger the filter was never attached to. If you attach the same filter to the root logger instead, the child record sails through unredacted.

Limits to keep in mind

  • The pattern matches the sume_live_ prefix followed by word characters and hyphens. If you log a header dict, make sure the value is a string the regex can see; a key split across fields is not matched.
  • Other handlers you add later, such as a Sentry or file handler, need the filter too.
  • Rotate the key if the raw value was ever written: create the new key, check it with GET /v1/me, deploy it, then revoke the old one.
  • A pre-commit secret scan catches keys in source; this filter catches keys at runtime.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume