Skip to content

Consider rate-limiting /api/auth/login to slow credential brute-forcing #3

Description

@gracejoi

What I found

The login endpoint accepts unlimited attempts — there's no throttling, backoff, lockout, or CAPTCHA anywhere on the path:

# src/web/app.py:492-508
@app.post("/api/auth/login", response_model=LoginResponse)
async def login(request: LoginRequest):
    """用户登录"""
    user = authenticate_user(request.username, request.password)
    if not user:
        from fastapi import HTTPException, status
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="用户名或密码错误",
        )
    token = create_access_token(user["username"])
    return LoginResponse(...)

Why this matters

The user table is a small, fixed set of well-known account names (admin, user1, user2 in src/web/auth.py:53-69). With no rate limit, an attacker can fire thousands of guesses per second at admin until a weak password falls. authenticate_user uses bcrypt (good — the per-attempt cost is non-trivial), but bcrypt alone is not a substitute for limiting attempts at the network edge.

This is hardening, not a crash bug — but for an endpoint that hands out 7-day admin tokens, it's worth doing before any internet-facing deployment.

Suggested next steps

  • Add per-IP (and ideally per-username) rate limiting. slowapi plugs into FastAPI cleanly:
    from slowapi import Limiter
    limiter = Limiter(key_func=get_remote_address)
    
    @app.post("/api/auth/login")
    @limiter.limit("5/minute")
    async def login(request: Request, body: LoginRequest):
        ...
  • Add a short exponential backoff / temporary lockout after N consecutive failures for a given username.
  • Keep the failure response generic (the current "用户名或密码错误" is good — it doesn't reveal whether the username exists).

Happy to send a PR wiring up slowapi if the maintainers are open to the dependency.

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