Skip to content

[SECURITY] UserRegister API accepts any password string β€” no length or complexity validationΒ #2218

@nyxsky404

Description

@nyxsky404

🐞 Description

The UserRegister Pydantic schema in backend/app/schemas.py accepts any value for the password field β€” including an empty string β€” with no length, format, or complexity validation. The backend bcrypt-hashes whatever it receives, meaning users can successfully register with password: "".


πŸ” Steps to Reproduce

  1. Start the backend server
  2. Send a POST /api/auth/register request with an empty or single-character password:
curl -X POST http://localhost:8000/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"username": "testuser", "email": "test@example.com", "password": ""}'
  1. Observe that the server returns 201 Created and a valid JWT token

πŸ’» Expected Behavior

The API should reject weak passwords with a 422 Unprocessable Entity response and a clear error message before the password ever reaches the bcrypt hashing layer.


🧠 Actual Behavior

backend/app/schemas.py:

class UserRegister(BaseModel):
    username: str
    email:    EmailStr
    password: str      # no min_length, no complexity requirement

Any string (including "") passes validation and gets stored as a bcrypt hash in the database.


πŸ–ΌοΈ Screenshots / Logs (if applicable)

File: backend/app/schemas.py β€” UserRegister class
The password field has no Field(min_length=...) constraint or validator.


🧩 Environment

  • Backend: FastAPI + Pydantic
  • File: backend/app/schemas.py

🧩 Possible Fix

Use Pydantic Field and field_validator to enforce minimum length and basic complexity:

from pydantic import BaseModel, EmailStr, Field, field_validator
import re

class UserRegister(BaseModel):
    username: str = Field(min_length=3, max_length=30)
    email:    EmailStr
    password: str = Field(min_length=8, max_length=128)

    @field_validator("password")
    @classmethod
    def password_complexity(cls, v: str) -> str:
        if not re.search(r"[A-Z]", v):
            raise ValueError("Password must contain at least one uppercase letter.")
        if not re.search(r"\d", v):
            raise ValueError("Password must contain at least one digit.")
        return v

Why it matters: Backend APIs must enforce their own invariants β€” frontend validation is bypassable via direct API calls. Bcrypt happily hashes an empty string, creating valid accounts with no real security.


βœ… Checklist

  • I have searched for existing issues before creating this one.
  • I have included all relevant details and reproduction steps.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions