feat: server-side password security checks

This commit is contained in:
2026-08-18 12:36:22 +07:00
parent 9e209dd695
commit 039babf540
4 changed files with 26 additions and 2 deletions

View File

@@ -21,14 +21,20 @@ class Settings(BaseSettings):
pally_shop_id: str = Field()
pally_token: str = Field()
### Remnawave ###
remnawave_base_url: str = Field()
remnawave_sub_url: str = Field()
remnawave_token: str = Field()
remnawave_default_squads_raw: str = Field(alias="REMNAWAVE_DEFAULT_SQUADS_UUIDS")
###
minimal_deposit: int = Field()
referal_bonus: int = Field(30)
### Security related ###
min_password_length: int = Field(8)
password_security_threshold: int = Field(2)
@computed_field
@property
def remnawave_default_squads(self) -> list[str]:

View File

@@ -6,6 +6,7 @@ from typing import Any
import jwt
from argon2 import PasswordHasher
from argon2.exceptions import InvalidHashError, VerificationError, VerifyMismatchError
from zxcvbn import zxcvbn
from config import cfg
from schemas.dto import KeyPair
@@ -52,3 +53,11 @@ def generate_pair(user_id: int, iss: ProvidersType) -> KeyPair:
def hash_refresh_token(token: str):
return hashlib.sha256(token.encode()).hexdigest()
def estimate_password_strength(password: str) -> bool:
if len(password) < cfg.min_password_length:
return False
r = zxcvbn(password)
return r.get("score", 0) > cfg.password_security_threshold

View File

@@ -9,3 +9,4 @@ alembic>=1.18.0
aiohttp>=3.14.0
python-multipart==0.0.32
remnawave>=2.6.1
zxcvbn>=4.5.0

View File

@@ -2,7 +2,12 @@ from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import JSONResponse
from sqlalchemy.ext.asyncio import AsyncSession
from core.secrets import hash_password, hash_refresh_token, verify_password
from core.secrets import (
estimate_password_strength,
hash_password,
hash_refresh_token,
verify_password,
)
from db.session import get_db
from repositories.sessions import SessionsRepository
from repositories.users import UserRepository
@@ -27,6 +32,9 @@ async def signup(req: UserRegistration, session: AsyncSession = Depends(get_db))
if user:
raise HTTPException(status_code=409, detail="User already exists")
if not estimate_password_strength(req.password):
raise HTTPException(422, detail="Password is not secure.")
password_hash = hash_password(req.password)
referal_id = None
if req.referal_code: