Admin auth: constant-time panel-token comparison in require_admin
Fixes review finding P2: require_admin compared the Bearer credential against config.panel_token with Python ==, which short-circuits and is not constant-time. Compare with secrets.compare_digest on UTF-8 bytes instead — the same pattern require_panel_token already uses for this exact secret — keeping the truthiness guard on config.panel_token and the same 403 rejection detail and behaviour.
This commit is contained in:
+9
-1
@@ -48,6 +48,7 @@ from __future__ import annotations
|
|||||||
import asyncio
|
import asyncio
|
||||||
import binascii
|
import binascii
|
||||||
import json
|
import json
|
||||||
|
import secrets
|
||||||
import time
|
import time
|
||||||
from collections.abc import Awaitable, Callable
|
from collections.abc import Awaitable, Callable
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
@@ -459,7 +460,14 @@ async def require_admin(
|
|||||||
if (
|
if (
|
||||||
config is not None
|
config is not None
|
||||||
and config.panel_token
|
and config.panel_token
|
||||||
and credential.strip() == config.panel_token
|
# compare_digest on bytes (as require_panel_token does)
|
||||||
|
# so the refusal does not short-circuit on the first
|
||||||
|
# mismatching byte — a constant-time comparison keeps a
|
||||||
|
# timing side channel on the credential off the table.
|
||||||
|
and secrets.compare_digest(
|
||||||
|
credential.strip().encode("utf-8"),
|
||||||
|
config.panel_token.encode("utf-8"),
|
||||||
|
)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=403,
|
status_code=403,
|
||||||
|
|||||||
Reference in New Issue
Block a user