Skip to content

Census API key logged in plaintext by geos.py, leaking into notebook HTML exports #3

Description

@jinskeep-morpc

Summary

morpc_census/geos.py logs request parameters after injecting the Census API key into them, so the key is written out in plaintext. morpc.logs.config_logs streams to the notebook cell output, so the key then ends up in any HTML written by morpc.notebook_to_html() and committed alongside the notebook.

Companion to morpc/morpc-py#160, which covers the same class of leak in morpc.req. Both need fixing: req.py logs params and r.url for every request, and geos.py logs the params itself before handing them over, so fixing either alone leaves the other leaking.

This is not hypothetical. morpc-osmbuildings-standardize.html currently has a live CENSUS_API_KEY in it, committed at 6045271 and pushed. I caught the same in morpc-bingbuildings-standardize before committing and held the export back. Both repos are private, so this is not public exposure, but the keys are live and in git history.

What it looks like

One INFO-level line from a routine county boundary fetch — fetch_geos_from_scope_sumlevel(scope="region15", sumlevel="county"):

INFO | morpc_census.geos.geoinfo_from_params: Getting GEOIDS from
https://api.census.gov/data/2024/geoinfo and params: {'get': 'GEO_ID,NAME',
'for': 'county:041, ...', 'in': 'state:39', 'key': '<40-char key, in full>'}

Affected lines

In morpc_census/geos.py, at 121650b:

Line Context Level
491 geoinfo_from_params — logs params immediately after L489-490 set params['key'] info
482 geoinfo_from_params — logger.error(f"ucgid without pseudo. {params}"), reachable after the key is set error
476 geoinfo_from_params — logs param_dict, which is caller-supplied and does not carry the key today debug

L491 is the one that actually leaks. geoids_from_scope (L511-512) also sets params['key'] but logs nothing itself — it leaks only through morpc.req, which is morpc/morpc-py#160.

Worth auditing at the same time, since all of them build params containing a key and pass them to morpc.req: geos.py:124 and api.py:177, 260, 336, 831, 897-900. None log directly right now, so they are only exposed via the req.py path, but they are one added log line away from the same bug.

Suggested fix

morpc/morpc-py#160 proposes exporting a _redact_params / _redact_url pair from morpc.req. If that lands, the fix here is to import it rather than reimplement:

from morpc.req import redact_params

logger.info(f"Getting GEOIDS from {url} and params: {redact_params(params)}.")

Redacting rather than dropping keeps the log useful — you can still see that a key was sent, which is what you want when a Census request comes back 403 or with an empty body.

If a fix is wanted here before morpc-py moves, the smaller version is to log the params before the key is injected, i.e. move L491 above L489. That fixes L491 specifically but not L482, and does nothing for the req.py path.

Also worth considering

Existing exports should be scrubbed and the affected keys rotated. A fix here stops new leaks but does nothing about what is already committed.

🤖 Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions