Files
6128aeaa89 fix(security): prevent TOTP reuse (#12616)
# Which Problems Are Solved

TOTP codes could be reused, violating [RFC 6238, section
5.2](https://datatracker.ietf.org/doc/html/rfc6238#section-5.2).

# How the Problems Are Solved

Transmitted codes are now recorded in each success event. As the codes
amount to secrets, they are not stored verbatim but as a randomly salted
HMAC-SHA256 (`crypto.HMACValue`). Each value gets a fresh random salt,
so an equal code always produces a different stored value and two
entries can never be recognized as being derived from the same code.

This is deliberately not a replacement for proper secret storage: with
the salt stored alongside the hash and a 6 digit payload, anyone with
event access can brute-force the code in milliseconds. That is
acceptable here because a code is worthless once its window has passed.
The `HMACValue` doc comment states the same.

TOTP codes are low-entropy (6 digits), pseudo random codes. Therefore it
is possible for the same code to be generated more than once over the
lifetime of a user. In order to prevent the (unlikely) case where users
login with large time intervalls and a generated code may be equal to
the one last used a long time ago, we only check for codes used within
the recent period window.

A simpler implementation would be simply storing the latest consumed
step, but the TOTP library does not return this value. Because of the
allowed skew there are always 3 steps within the validation window, so
we can't calculate the current step based on time alone. The retained
history spans one period more than the validation window: validity is
decided by the app server's clock, while the stored events carry the
database clock, and the extra period absorbs the drift between them.

**Note:** a reused code pushes a distinct
`user.human.mfa.otp.check.reused` event, not a failed check event. A
replay therefore stays observable through the Events API without
counting towards the lockout policy. Otherwise anyone who captured a
code could replay it to lock the user out.

# Additional Changes

- Read only the latest events in recheck query

# Additional Context

- Closes https://github.com/zitadel/zitadel/issues/6452

---------

Co-authored-by: Livio Spring <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: muhlemmer <[email protected]>
2026-08-27 07:17:39 +02:00

541 lines
29 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Login:
Title: Добро пожаловать!
Description: Введите свои данные для входа.
TitleLinking: Вход для привязки пользователей
DescriptionLinking: Введите данные для входа, чтобы привязать внешнего пользователя к учётной записи Zitadel.
LoginNameLabel: Логин
UsernamePlaceHolder: логин
LoginnamePlaceHolder: username@domain
ExternalUserDescription: Войти как внешний пользователь.
MustBeMemberOfOrg: Пользователь должен быть членом организации {{.OrgName}}.
RegisterButtonText: Зарегистрироваться
NextButtonText: Продолжить
LDAP:
Title: Войти
Description: Введите свои данные для входа.
LoginNameLabel: Логин
PasswordLabel: Пароль
NextButtonText: Продолжить
SelectAccount:
Title: Выбор учётной записи
Description: Выберите учётную запись.
TitleLinking: Выберите учётную запись для привязки пользователя
DescriptionLinking: Выберите свою учётную запись для связи с внешним пользователем.
OtherUser: Другая учётная запись
SessionState0: активный
SessionState1: неактивный
MustBeMemberOfOrg: Пользователь должен быть членом организации {{.OrgName}}.
Password:
Title: Пароль
Description: Введите данные для входа.
PasswordLabel: Пароль
MinLength: Минимум
MinLengthp2: символов.
MaxLength: Не более 70 символов.
HasUppercase: Должен содержать заглавную букву.
HasLowercase: Должен содержать строчную букву.
HasNumber: Должен содержать цифру.
HasSymbol: Должен содержить специальный символ.
Confirmation: Пароли должны совпадать.
ResetLinkText: Сбросить пароль
BackButtonText: Назад
NextButtonText: Продолжить
UsernameChange:
Title: Изменение логина
Description: Установите новый логин.
UsernameLabel: Логин
CancelButtonText: Отмена
NextButtonText: Продолжить
UsernameChangeDone:
Title: Логин изменён
Description: Ваш логин был успешно изменён.
NextButtonText: Продолжить
InitPassword:
Title: Установка пароля
Description: Введите код из письма, отправленного на вашу электронную почту, чтобы установить пароль.
CodeLabel: Код из письма
NewPasswordLabel: Новый пароль
NewPasswordConfirmLabel: Повторите пароль
ResendButtonText: Отправить код ещё раз
NextButtonText: Продолжить
InitPasswordDone:
Title: Пароль установлен
Description: Пароль успешно установлен.
NextButtonText: Продолжить
CancelButtonText: Отмена
InitUser:
Title: Активация учётной записи
Description: Введите код из письма для подтверждения электронной почты и установите новый пароль.
CodeLabel: Код из письма
NewPasswordLabel: Новый пароль
NewPasswordConfirm: Повторите пароль
NextButtonText: Продолжить
ResendButtonText: Отправить код ещё раз
InitUserDone:
Title: Учётная запись активирована
Description: Электронная почта подтверждена и пароль успешно установлен.
NextButtonText: Продолжить
CancelButtonText: Отмена
InviteUser:
Title: Активация учётной записи
Description: Введите код из письма для подтверждения электронной почты и установите новый пароль.
CodeLabel: Код из письма
NewPasswordLabel: Новый пароль
NewPasswordConfirm: Повторите пароль
NextButtonText: Продолжить
ResendButtonText: Отправить код ещё раз
InitMFAPrompt:
Title: Установка двухфакторной аутентификации
Description: Установите двухфакторную аутентификацию для дополнительной защиты вашей учётной записи.
Provider0: Приложение для кодов (например, Google/Microsoft Authenticator или Authy)
Provider1: С помощью устройства (Face ID, Windows Hello, отпечаток пальца)
Provider3: Получать код по СМС
Provider4: Получать код по электронной почте
NextButtonText: Продолжить
SkipButtonText: Пропустить
InitMFAOTP:
Title: Настройка двухфакторной аутентификации
Description: Настройте двухфакторную аутентификацию. Загрузите приложение для генерации кодов, если оно у вас отсутствует.
OTPDescription: Отсканируйте QR-код с помощью приложения (например, Google Authenticator, Microsoft Authenticator или Authy), либо скопируйте секретный ключ и введите код ниже.
SecretLabel: Секретный ключ
CodeLabel: Код подтверждения
NextButtonText: Продолжить
CancelButtonText: Отмена
InitMFAOTPSMS:
Title: Настройка двухфакторной аутентификации
DescriptionPhone: Введите номер телефона для его подтверждения.
DescriptionCode: Введите код из СМС для подтверждения номера телефона.
CountryCodeLabel: Страна
PhoneLabel: Номер телефона
CodeLabel: Код из СМС
EditButtonText: Изменить
ResendButtonText: Отправить код ещё раз
NextButtonText: Продолжить
InitMFAU2F:
Title: Добавление ключа безопасности
Description: Ключ безопасности — это метод проверки, который может быть встроен в телефон, использовать Bluetooth или подключаться напрямую к USB-порту вашего компьютера.
TokenNameLabel: Название ключа безопасности / устройства
NotSupported: Ваш браузер не поддерживает WebAuthN. Пожалуйста, обновите его или используйте другой (например, Chrome, Safari, Firefox).
RegisterTokenButtonText: Добавить ключ безопасности
ErrorRetry: Повторите попытку, создайте новый запрос или выберите другой метод.
InitMFADone:
Title: Ключ безопасности подтверждён
Description: Отлично! Вы успешно настроили двухфакторную аутентификацию и сделали свою учётную запись более безопасной. Фактор необходимо вводить при каждом входе в систему.
NextButtonText: Продолжить
CancelButtonText: Отмена
MFAProvider:
Provider0: Приложение для кодов (например, Google/Microsoft Authenticator или Authy)
Provider1: С помощью устройства (Face ID, Windows Hello, отпечаток пальца)
Provider3: Получать код по СМС
Provider4: Получать код по электронной почте
ChooseOther: или выберите другой вариант
VerifyMFAOTP:
Title: Подтверждение двухфакторной аутентификации
Description: Введите код для проверки второго фактора
CodeLabel: Код
NextButtonText: Продолжить
VerifyOTP:
Title: Подтверждение двухфакторной аутентификации
Description: Введите код для проверки второго фактора
CodeLabel: Код
ResendButtonText: Отправить код ещё раз
NextButtonText: Продолжить
VerifyMFAU2F:
Title: Подтверждение двухфакторной аутентификации
Description: Подтвердите двухфакторную аутентификацию с помощью зарегистрированного устройства (например, FaceID, Windows Hello или отпечатка пальца).
NotSupported: Ваш браузер не поддерживает WebAuthN. Пожалуйста, обновите его или используйте другой (например, Chrome, Safari, Firefox).
ErrorRetry: Повторите попытку или выберите другой метод.
ValidateTokenButtonText: Подтвердить
Passwordless:
Title: Вход без пароля
Description: Войдите в систему с помощью методов аутентификации, доступных на вашем устройстве, таких как FaceID, Windows Hello или отпечаток пальца.
NotSupported: Ваш браузер не поддерживает WebAuthN. Пожалуйста, обновите его или используйте другой (например, Chrome, Safari, Firefox).
ErrorRetry: Повторите попытку или выберите другой метод.
LoginWithPwButtonText: Войти с паролем
ValidateTokenButtonText: Войти без пароля
PasswordlessPrompt:
Title: Настройка входа без пароля
Description: Хотите настроить вход без пароля? Вы сможете использовать методы аутентификации вашего устройства, такие как FaceID, Windows Hello или отпечаток пальца.
DescriptionInit: Для начала настройки входа без пароля перейдите по полученной ссылке и зарегистрируйте своё устройство.
PasswordlessButtonText: Настроить вход без пароля
NextButtonText: Продолжить
SkipButtonText: Пропустить
PasswordlessRegistration:
Title: Настройка входа без пароля
Description: Укажите имя устройства (например, MyMobilePhone, MacBook и так далее), а затем нажмите кнопку «Зарегистрировать вход без пароля» ниже.
TokenNameLabel: Имя устройства
NotSupported: Ваш браузер не поддерживает WebAuthN. Пожалуйста, обновите его или используйте другой (например, Chrome, Safari, Firefox).
RegisterTokenButtonText: Зарегистрировать вход без пароля
ErrorRetry: Повторите попытку или выберите другой метод.
PasswordlessRegistrationDone:
Title: Вход без пароля настроен
Description: Устройство для входа без пароля успешно зарегистрировано.
DescriptionClose: Теперь вы можете закрыть это окно.
NextButtonText: Продолжить
CancelButtonText: Отмена
PasswordChange:
Title: Изменение пароля
Description: Введите текущий пароль и задайте новый.
ExpiredDescription: Срок действия вашего пароля истёк. Пожалуйста, введите текущий пароль и задайте новый.
OldPasswordLabel: Текущий пароль
NewPasswordLabel: Новый пароль
NewPasswordConfirmLabel: Повторите новый пароль
CancelButtonText: Отмена
NextButtonText: Продолжить
Footer: Примечание
PasswordChangeDone:
Title: Изменение пароля
Description: Ваш пароль был успешно изменён.
NextButtonText: Продолжить
PasswordResetDone:
Title: Ссылка для сброса пароля отправлена
Description: Проверьте вашу электронную почту, чтобы сбросить пароль.
NextButtonText: Продолжить
EmailVerification:
Title: Подтверждение электронной почты
Description: Мы отправили письмо с кодом для подтверждения вашей электронной почты. Введите код ниже.
CodeLabel: Код
NextButtonText: Продолжить
ResendButtonText: Отправить код ещё раз
EmailVerificationDone:
Title: Подтверждение электронной почты
Description: Ваша электронная почта успешно подтверждена.
NextButtonText: Продолжить
CancelButtonText: Отмена
LoginButtonText: Войти
RegisterOption:
Title: Способы регистрации
Description: Выберите способ регистрации.
RegisterUsernamePasswordButtonText: С логином и паролем
ExternalLoginDescription: или зарегистрируйтесь с помощью внешнего пользователя
LoginButtonText: Войти
RegistrationUser:
Title: Регистрация
Description: Введите свои данные. Электронная почта будет использоваться как логин.
DescriptionOrgRegister: Введите свои данные.
EmailLabel: Электронная почта
UsernameLabel: Логин
FirstnameLabel: Имя
LastnameLabel: Фамилия
LanguageLabel: Язык
German: Deutsch
English: English
Italian: Italiano
French: Français
Chinese: 简体中文
Polish: Polski
Japanese: 日本語
Spanish: Español
Bulgarian: Български
Portuguese: Português
Macedonian: Македонски
Czech: Čeština
Russian: Русский
Dutch: Nederlands
Swedish: Svenska
Indonesian: Bahasa Indonesia
Hungarian: Magyar
Korean: 한국어
Romanian: Română
Turkish: Türkçe
Ukrainian: Українська
Arabic: العربية
GenderLabel: Пол
Female: Женский
Male: Мужской
Diverse: Другой / X
PasswordLabel: Пароль
PasswordConfirmLabel: Повторите пароль
TosAndPrivacyLabel: Условия использования
TosConfirm: Я согласен с
TosLinkText: Пользовательским соглашением
PrivacyConfirm: Я согласен с
PrivacyLinkText: Политикой конфиденциальности
ExternalLogin: или зарегистрируйтесь с помощью внешнего пользователя
BackButtonText: Войти
NextButtonText: Продолжить
ExternalRegistrationUserOverview:
Title: Регистрация внешнего пользователя
Description: Мы получили ваши данные от провайдера. Вы можете изменить или дополнить их.
EmailLabel: Электронная почта
UsernameLabel: Логин
FirstnameLabel: Имя
LastnameLabel: Фамилия
NicknameLabel: Псевдоним
PhoneLabel: Номер телефона
LanguageLabel: Язык
German: Deutsch
English: English
Italian: Italiano
French: Français
Chinese: 简体中文
Polish: Polski
Japanese: 日本語
Spanish: Español
Bulgarian: Български
Portuguese: Português
Macedonian: Македонски
Czech: Čeština
Russian: Русский
Dutch: Nederlands
Swedish: Svenska
Indonesian: Bahasa Indonesia
Hungarian: Magyar
Korean: 한국어
Romanian: Română
Turkish: Türkçe
Ukrainian: Українська
Arabic: العربية
TosAndPrivacyLabel: Условия использования
TosConfirm: Я согласен с
TosLinkText: Пользовательским соглашением
PrivacyConfirm: Я согласен с
PrivacyLinkText: Политикой конфиденциальности
ExternalLogin: или зарегистрируйтесь с помощью внешнего пользователя
BackButtonText: Назад
NextButtonText: Сохранить
RegistrationOrg:
Title: Регистрация организации
Description: Введите название вашей организации и данные пользователя.
OrgNameLabel: Название организации
EmailLabel: Электронная почта
UsernameLabel: Логин
FirstnameLabel: Имя
LastnameLabel: Фамилия
PasswordLabel: Пароль
PasswordConfirmLabel: Повторите пароль
TosAndPrivacyLabel: Условия использования
TosConfirm: Я согласен с
TosLinkText: Пользовательским соглашением
PrivacyConfirm: Я согласен с
PrivacyLinkText: Политикой конфиденциальности
SaveButtonText: Создать организацию
LoginSuccess:
Title: Успешный вход
AutoRedirectDescription: Вы будете автоматически перенаправлены в приложение. Если этого не произошло, нажмите кнопку ниже.
RedirectedDescription: Вы можете закрыть это окно.
NextButtonText: Продолжить
LogoutDone:
Title: Выход из системы
Description: Вы успешно вышли из системы.
LoginButtonText: Войти
LinkingUserPrompt:
Title: Найден существующий пользователь
Description: "Хотите связать эту учётную запись с существующей?"
LinkButtonText: Связать
OtherButtonText: Другие варианты
LinkingUsersDone:
Title: Привязка пользователя
Description: Привязка учётной записи выполнена успешно.
CancelButtonText: Отмена
NextButtonText: Продолжить
ExternalNotFound:
Title: Внешний пользователь не найден
Description: Мы не смогли найти указанного внешнего пользователя. Вы можете привязать существующую учетную запись или зарегистрировать нового пользователя.
LinkButtonText: Привязать
AutoRegisterButtonText: Зарегистрировать
TosAndPrivacyLabel: Условия использования
TosConfirm: Я согласен с
TosLinkText: Пользовательским соглашением
PrivacyConfirm: Я согласен с
PrivacyLinkText: Политикой конфиденциальности
German: Deutsch
English: English
Italian: Italiano
French: Français
Chinese: 简体中文
Polish: Polski
Japanese: 日本語
Spanish: Español
Bulgarian: Български
Portuguese: Português
Macedonian: Македонски
Czech: Čeština
Russian: Русский
Dutch: Nederlands
Swedish: Svenska
Indonesian: Bahasa Indonesia
Hungarian: Magyar
Korean: 한국어
Romanian: Română
Turkish: Türkçe
Ukrainian: Українська
Arabic: العربية
DeviceAuth:
Title: Авторизация устройства
UserCode:
Label: Код пользователя
Description: Введите код, отображаемый на вашем устройстве.
ButtonNext: Продолжить
Action:
Description: Предоставьте устройству доступ к системе.
GrantDevice: вы собираетесь предоставить устройству
AccessToScopes: доступ к следующим областям
Button:
Allow: Разрешать
Deny: Отклонить
Done:
Description: Операция завершена.
Approved: Устройство успешно авторизовано. Теперь вы можете вернуться к устройству.
Denied: Авторизация устройства отклонена. Теперь вы можете вернуться к устройству.
Footer:
PoweredBy: Работает на основе
Tos: Пользовательское соглашение
PrivacyPolicy: Политика конфиденциальности
Help: Помощь
SupportEmail: Электронная почта службы поддержки
SignIn: Войти с помощью {{.Provider}}
Errors:
Internal: Произошла внутренняя ошибка
AuthRequest:
NotFound: authrequest не найден
UserAgentNotCorresponding: User Agent не совпадает
UserAgentNotFound: User Agent ID не найден
TokenNotFound: Токен не найден
RequestTypeNotSupported: Тип запроса не поддерживается
MissingParameters: Отсутствуют обязательные параметры
User:
NotFound: Пользователь не найден
AlreadyExists: Пользователь уже существует
Inactive: Пользователь неактивен
NotFoundOnOrg: Не удалось найти пользователя в выбранной организации
NotAllowedOrg: Пользователь не является членом требуемой организации
NotMatchingUserID: Указанный пользователь не совпадает с пользователем в authrequest
UserIDMissing: Не указан UserID
Invalid: Неверные данные пользователя
DomainNotAllowedAsUsername: Домен уже зарезервирован и не может быть использован в качестве логина
NotAllowedToLink: Привязка пользователя к внешнему провайдеру запрещена
Profile:
NotFound: Профиль не найден
NotChanged: Профиль не изменен
Empty: Профиль пуст
FirstNameEmpty: Имя в профиле пустое
LastNameEmpty: Фамилия в профиле пустая
IDMissing: Отсутствует идентификатор профиля
Email:
NotFound: Электронная почта не найдена
Invalid: Некорректный адрес электронной почты
AlreadyVerified: Электронная почта уже подтверждена
NotChanged: Адрес электронной почты не изменился
Empty: Электронная почта не указана
IDMissing: Отсутствует идентификатор электронной почты
Phone:
NotFound: Номер телефона не найден
Invalid: Неверный номер телефона
AlreadyVerified: Номер телефона уже подтвержден
Empty: Номер телефона не указан
NotChanged: Номер телефона не изменился
Address:
NotFound: Адрес не найден
NotChanged: Адрес не изменился
Username:
AlreadyExists: Логин уже занят
Reserved: Логин уже занят
Empty: Логин не указан
Password:
ConfirmationWrong: Указанные пароли не совпадают
Empty: Пароль не указан
Invalid: Неверный пароль
InvalidAndLocked: Неверный пароль, пользователь заблокирован. Обратитесь к администратору.
NotChanged: Пароль не изменился
UsernameOrPassword:
Invalid: Неверный логин или пароль
PasswordComplexityPolicy:
NotFound: Политика паролей не найдена
MinLength: Пароль слишком короткий
HasLower: Пароль должен содержать хотя бы одну строчную букву
HasUpper: Пароль должен содержать хотя бы одну заглавную букву
HasNumber: Пароль должен содержать хотя бы одну цифру
HasSymbol: Пароль должен содержать хотя бы один специальный символ
Code:
Expired: Код истёк
Invalid: Неверный код
Empty: Код не указан
CryptoCodeNil: Пустой криптокод
NotFound: Код не найден
GeneratorAlgNotSupported: Алгоритм генерации кода не поддерживается
EmailVerify:
UserIDEmpty: UserID не указан
ExternalData:
CouldNotRead: Не удалось обработать внешние данные
MFA:
NoProviders: Нет доступных методов многофакторной аутентификации
OTP:
AlreadyReady: OTP (OneTimePassword) уже настроен
NotExisting: OTP (OneTimePassword) не существует
InvalidCode: Неверный код
Reused: Этот код уже был использован. Пожалуйста, дождитесь нового кода
NotReady: OTP (OneTimePassword) не готов
Locked: Пользователь заблокирован
SomethingWentWrong: Что-то пошло не так
NotActive: Пользователь неактивен
ExternalIDP:
IDPTypeNotImplemented: Тип внешнего провайдера не поддерживается
NotAllowed: Доступ к внешнему провайдеру запрещен
IDPConfigIDEmpty: Не указан идентификатор конфигурации провайдера
ExternalUserIDEmpty: Не указан внешний идентификатор пользователя
UserDisplayNameEmpty: Отображаемое имя пользователя не указано
NoExternalUserData: Внешние данные пользователя отсутствуют
CreationNotAllowed: Создание нового пользователя для этого провайдера запрещено
LinkingNotAllowed: Привязка к этому провайдеру запрещена
NoOptionAllowed: Ни создание, ни привязка пользователя к этому провайдеру невозможны. Обратитесь к администратору.
LoginFailedSwitchLocal: |
Вход в внешний поставщик идентификации не удался. Возвращаемся к локальному входу.
Подробности об ошибке: {{.Details}}
GrantRequired: Вход невозможен. Пользователь должен иметь хотя бы один допуск к приложению. Обратитесь к администратору.
ProjectRequired: Вход невозможен. Организация пользователя должна иметь доступ к проекту. Обратитесь к администратору.
IdentityProvider:
InvalidConfig: Некорректная конфигурация провайдера идентификации
IAM:
LockoutPolicy:
NotExisting: Политика блокировки не найдена
Org:
LoginPolicy:
RegistrationNotAllowed: Регистрация запрещена
DeviceAuth:
NotExisting: Код пользователя не существует
optional: (optional)