Repository navigation
Description
Summary
Severity: Low
Area: Security / auth
Affected files: agentkit/api/auth.py:22-30, agentkit/config.py:127-130
Suggested branch: fix/api-key-constant-time-compare
Suggested PR title: fix(auth): compare API keys in constant time
This is finding AUDIT-17 of 18 from a consolidated audit of agentkit/. It is deliberately self-contained so it can be fixed on its own branch with its own PR.
Related findings: Independent of the other findings.
AUDIT-17 — Low — API key comparison is not constant-time and not normalised
File — agentkit/api/auth.py:22-30
async def verify_api_key(
api_key: str | None = Security(api_key_header),
settings: Settings = Depends(get_current_settings),
) -> str:
"""Validate client API key from X-API-Key header against configured authorized keys."""
if not api_key or api_key not in settings.api_keys_list:
logger.warning("Unauthorized API request with missing or invalid key.")
raise AuthenticationError("Invalid or missing API key.")
return api_keyWhy this is a bug
in on a list is a short-circuiting, timing-variable comparison — a classic timing oracle for recovering a valid key byte-by-byte. Additionally, configured keys are stripped (Settings.api_keys_list, config.py:127-130) but the presented key is not, so " key " fails while "key" succeeds (an inconsistency that also weakens logging hygiene, since the raw key is used as the rate-limit key).
Pass cases
- Key comparison uses a constant-time comparison (
hmac.compare_digest) over all configured keys. - The presented key is normalised (stripped) the same way as configured keys, and the normalised value is what is used for rate limiting and logging.
- Existing auth tests (missing key, wrong key, valid key) still pass; a test asserts
" key "and"key"behave identically.
Definition of done (per AGENTS.md)
- New/updated unit and integration tests covering the pass cases above, all passing (
pytest). -
ruff check .,ruff format --check .andmypy agentkitclean. - Docstrings updated where behaviour changed;
docs/PROGRESS.mdupdated (status, PR link, commit count, notes). - A new ADR in
docs/DECISIONS.mdif an architectural decision was made. - Branch pushed and PR opened (never commit directly to
main).
Baseline at the time of writing: pytest is green on main (all tests pass; tests/real/* skipped unless AGENTKIT_REAL_INFRA=1).
Activity
getdownandcode commentedon Oct 20, 2026
Audit index (18 separate issues, fix one by one)
Each issue is self-contained: current code, reproduction, pass cases, suggested branch and PR title. Reference the relevant one with
Fixes #NNin your PR.