diff --git a/moderation/backend-py/routes/moderation_auth.py b/moderation/backend-py/routes/moderation_auth.py index 4d219f8..e208c2f 100644 --- a/moderation/backend-py/routes/moderation_auth.py +++ b/moderation/backend-py/routes/moderation_auth.py @@ -202,15 +202,48 @@ async def register(request: RegisterRequest, response: Response): detail="Аккаунт уже зарегистрирован. Используйте вход по паролю." ) - # Check password strength - if len(request.password) < 6: + # Check password strength and length + password_len = len(request.password) + print(f"[ModerationAuth] 🔍 Проверка пароля:") + print(f"[ModerationAuth] Длина в символах: {password_len}") + print(f"[ModerationAuth] Пароль (repr): {repr(request.password)}") + print(f"[ModerationAuth] Пароль (raw): {request.password}") + + if password_len < 8: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, - detail="Пароль должен содержать минимум 6 символов" + detail="Пароль должен содержать минимум 8 символов" ) + if password_len > 24: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Пароль должен содержать максимум 24 символа" + ) + + # Check password length in bytes (bcrypt limit is 72 bytes) + # Some special characters may take more than 1 byte in UTF-8 (например, эмодзи, кириллица) + password_bytes = request.password.encode('utf-8') + password_bytes_len = len(password_bytes) + print(f"[ModerationAuth] Длина в байтах (UTF-8): {password_bytes_len}") + print(f"[ModerationAuth] Байты (hex): {password_bytes.hex()}") + + # Убрали проверку на 72 байта - пусть hash_password() сам обрежет + # Это позволит избежать ошибок для нормальных паролей + if password_bytes_len > 72: + print(f"[ModerationAuth] ⚠️ Пароль превышает 72 байта ({password_bytes_len} байт), будет обрезан при хешировании") + # Hash password - password_hash = hash_password(request.password) + try: + password_hash = hash_password(request.password) + except Exception as hash_error: + print(f"[ModerationAuth] ❌ Ошибка при хешировании пароля: {type(hash_error).__name__}: {hash_error}") + import traceback + traceback.print_exc() + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Ошибка при создании пароля: {str(hash_error)}" + ) # Update user await users_collection().update_one( diff --git a/moderation/backend-py/utils/auth.py b/moderation/backend-py/utils/auth.py index e572074..82305d7 100644 --- a/moderation/backend-py/utils/auth.py +++ b/moderation/backend-py/utils/auth.py @@ -19,12 +19,64 @@ security = HTTPBearer(auto_error=False) def hash_password(password: str) -> str: - """Hash password with bcrypt""" - return pwd_context.hash(password) + """ + Hash password with bcrypt + + Bcrypt имеет ограничение: пароль не может быть длиннее 72 байт. + Если пароль превышает 72 байта, он будет автоматически обрезан до первых 72 байт. + + ВАЖНО: Если пароль обрезан при регистрации, то при входе нужно ввести + тот же пароль (который также будет обрезан до 72 байт), иначе проверка не сработает. + + Для избежания проблем рекомендуется использовать пароли до 24 символов, + что обычно не превышает 72 байта даже для UTF-8 символов. + """ + # Bcrypt has a maximum password length of 72 bytes + # Truncate if necessary (encode to bytes first to get accurate byte length) + print(f"[Auth] 🔍 hash_password вызван:") + print(f"[Auth] Пароль (repr): {repr(password)}") + print(f"[Auth] Длина в символах: {len(password)}") + + password_bytes = password.encode('utf-8') + original_length = len(password_bytes) + print(f"[Auth] Длина в байтах: {original_length}") + print(f"[Auth] Байты (hex): {password_bytes.hex()}") + + if original_length > 72: + # Обрезаем до первых 72 байт + password = password_bytes[:72].decode('utf-8', errors='ignore') + print(f"[Auth] ⚠️ Пароль обрезан до 72 байт (было {original_length} байт)") + print(f"[Auth] ⚠️ ВНИМАНИЕ: При входе используйте тот же пароль, он также будет обрезан") + else: + print(f"[Auth] ✅ Пароль в пределах лимита (72 байта)") + + try: + result = pwd_context.hash(password) + print(f"[Auth] ✅ Пароль успешно захеширован") + return result + except Exception as e: + print(f"[Auth] ❌ Ошибка при хешировании: {type(e).__name__}: {e}") + import traceback + traceback.print_exc() + raise def verify_password(plain_password: str, hashed_password: str) -> bool: - """Verify password against hash""" + """ + Verify password against hash + + Bcrypt имеет ограничение: пароль не может быть длиннее 72 байт. + Если пароль превышает 72 байта, он будет автоматически обрезан до первых 72 байт. + + Это гарантирует, что проверка сработает даже если пароль был обрезан при регистрации. + """ + # Bcrypt has a maximum password length of 72 bytes + # Truncate if necessary (encode to bytes first to get accurate byte length) + password_bytes = plain_password.encode('utf-8') + if len(password_bytes) > 72: + # Обрезаем до первых 72 байт (так же, как при хешировании) + plain_password = password_bytes[:72].decode('utf-8', errors='ignore') + print(f"[Auth] ⚠️ Пароль при проверке обрезан до 72 байт (было {len(password_bytes)} байт)") return pwd_context.verify(plain_password, hashed_password) diff --git a/moderation/frontend/src/App.jsx b/moderation/frontend/src/App.jsx index fbd3088..421c718 100644 --- a/moderation/frontend/src/App.jsx +++ b/moderation/frontend/src/App.jsx @@ -736,8 +736,13 @@ export default function App() { return; } - if (authForm.password.length < 6) { - setError('Пароль должен содержать минимум 6 символов'); + if (authForm.password.length < 8) { + setError('Пароль должен содержать минимум 8 символов'); + return; + } + + if (authForm.password.length > 24) { + setError('Пароль должен содержать максимум 24 символа'); return; } @@ -1547,7 +1552,7 @@ export default function App() { type={authForm.showPassword ? 'text' : 'password'} value={authForm.password} onChange={(e) => setAuthForm((prev) => ({ ...prev, password: e.target.value }))} - placeholder="Минимум 6 символов" + placeholder="От 8 до 24 символов" style={{ padding: '12px', borderRadius: '8px',