Skip to content

server: login allows username enumeration via response-timing side-channel #529

Description

@peteski22

Summary

AuthService.login (server/backend/src/cq_server/services/auth.py) short-circuits password verification when the username is unknown, so an unknown-username request returns measurably faster than a known-username/wrong-password request. This is a timing oracle for username enumeration.

Where

server/backend/src/cq_server/services/auth.py:

user = await self._users.get(username)
if user is None or not verify_password(password, user["password_hash"]):
    raise InvalidCredentialsError()

verify_password is bcrypt.checkpw (deliberately slow). When user is None, or short-circuits and bcrypt never runs, so the response is fast; when the user exists, bcrypt runs and the response is slow.

Impact

Low severity, but real: the uniform 401 and "Invalid username or password" message correctly closes the content channel (there is no message difference between unknown-user and wrong-password), but the timing channel remains open. An unauthenticated attacker can distinguish valid usernames by latency.

Suggested fix

Equalize the work regardless of whether the user exists: when user is None, still run a bcrypt verify against a fixed dummy hash of the same cost, then fail. This keeps both paths on the same code cost. A test asserting both branches call verify_password would lock it in.

Context

Surfaced while reviewing #525 (a frontend-only error-message change, which correctly did not touch this). The message-channel enumeration protection lives in the backend here; this issue is only about the timing channel.

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

    backendbugSomething isn't workingserverFor issues or PRs related to the 'server' component.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions