Contexto
Follow-up do #1143 (fix constant-time do #1134). Ao trocar provided == expected por hmac.compare_digest(provided, expected) em api/rbac.py (~linha 176, resolve_effective_roles_for_request), abriu-se uma aresta de robustez.
Problema
hmac.compare_digest(a, b) exige que os dois args sejam str ASCII-only (ou bytes). Se o atacante mandar uma API key não-ASCII no header (X-API-Key ou Authorization: Bearer … com acento/emoji/byte alto), o compare_digest levanta TypeError → não tratado → HTTP 500.
- NÃO é bypass de auth — não retorna roles, o request não passa. É robustez: 500 em vez de 401.
- Severidade LOW/P3: input malformado do atacante causa erro-de-servidor (não vaza nada sensível; no máximo sinaliza que o path de compare foi alcançado).
- O
== antigo não tinha isso (comparava qualquer str) — é regressão de robustez introduzida pelo hardening.
Fix (preserva constant-time)
Comparar em bytes (sem a restrição ASCII do str):
if provided and hmac.compare_digest(provided.encode("utf-8"), expected.encode("utf-8")):
...
Non-ASCII vira bytes distintos de expected → compare_digest retorna False (não-match), sem crash e ainda constant-time. Alternativa: try/except TypeError tratando como não-match (menos limpo).
AC
api/rbac.py: comparar em bytes (.encode()) no path da API key.
- Teste: request com
X-API-Key não-ASCII (ex: "café-🔑") → 401 (não-match), não 500.
- Confirmar que o teste de constant-time existente (
test_rbac_api_key_compare_uses_hmac_compare_digest) segue verde.
Contexto
Follow-up do #1143 (fix constant-time do #1134). Ao trocar
provided == expectedporhmac.compare_digest(provided, expected)emapi/rbac.py(~linha 176,resolve_effective_roles_for_request), abriu-se uma aresta de robustez.Problema
hmac.compare_digest(a, b)exige que os dois args sejam str ASCII-only (ou bytes). Se o atacante mandar uma API key não-ASCII no header (X-API-KeyouAuthorization: Bearer …com acento/emoji/byte alto), ocompare_digestlevantaTypeError→ não tratado → HTTP 500.==antigo não tinha isso (comparava qualquer str) — é regressão de robustez introduzida pelo hardening.Fix (preserva constant-time)
Comparar em bytes (sem a restrição ASCII do str):
Non-ASCII vira bytes distintos de
expected→compare_digestretornaFalse(não-match), sem crash e ainda constant-time. Alternativa:try/except TypeErrortratando como não-match (menos limpo).AC
api/rbac.py: comparar em bytes (.encode()) no path da API key.X-API-Keynão-ASCII (ex:"café-🔑") → 401 (não-match), não 500.test_rbac_api_key_compare_uses_hmac_compare_digest) segue verde.