mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-03 12:52:16 +00:00
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]>
This commit is contained in:
co-authored by
Livio Spring
Copilot Autofix powered by AI
copilot-swe-agent[bot] <198982749+[email protected]>
muhlemmer
parent
c9d747d407
commit
6128aeaa89
@@ -500,6 +500,7 @@ Errors:
|
||||
AlreadyReady: المصادقة متعددة العوامل OTP (كلمة مرور لمرة واحدة) معدة بالفعل
|
||||
NotExisting: المصادقة متعددة العوامل OTP (كلمة مرور لمرة واحدة) غير موجودة
|
||||
InvalidCode: رمز غير صالح
|
||||
Reused: تم استخدام هذا الرمز بالفعل. يرجى انتظار رمز جديد
|
||||
NotReady: المصادقة متعددة العوامل OTP (كلمة مرور لمرة واحدة) ليست جاهزة
|
||||
Locked: المستخدم مقفل
|
||||
SomethingWentWrong: حدث خطأ ما
|
||||
|
||||
@@ -492,6 +492,7 @@ Errors:
|
||||
AlreadyReady: Многофакторният OTP (OneTimePassword) вече е настроен
|
||||
NotExisting: Многофакторният OTP (OneTimePassword) не съществува
|
||||
InvalidCode: Невалиден код
|
||||
Reused: Този код вече е използван. Моля, изчакайте нов код
|
||||
NotReady: Многофакторният OTP (OneTimePassword) не е готов
|
||||
Locked: Потребителят е заключен
|
||||
SomethingWentWrong: Нещо се обърка
|
||||
|
||||
@@ -504,6 +504,7 @@ Errors:
|
||||
AlreadyReady: Vícefaktorové OTP (jednorázové heslo) je již nastaveno
|
||||
NotExisting: Vícefaktorové OTP (jednorázové heslo) neexistuje
|
||||
InvalidCode: Neplatný kód
|
||||
Reused: Tento kód již byl použit. Počkejte prosím na nový kód
|
||||
NotReady: Vícefaktorové OTP (jednorázové heslo) není připraveno
|
||||
Locked: Uživatel je uzamčen
|
||||
SomethingWentWrong: Něco se pokazilo
|
||||
|
||||
@@ -503,6 +503,7 @@ Errors:
|
||||
AlreadyReady: Multifaktor OTP (OneTimePassword) ist bereits eingerichtet
|
||||
NotExisting: Multifaktor OTP (OneTimePassword) existiert nicht
|
||||
InvalidCode: Code ist ungültig
|
||||
Reused: Dieser Code wurde bereits verwendet. Bitte warten Sie auf einen neuen Code
|
||||
NotReady: Multifaktor OTP (OneTimePassword) ist nicht bereit
|
||||
Locked: Benutzer ist gesperrt
|
||||
SomethingWentWrong: Irgendetwas ist schief gelaufen
|
||||
|
||||
@@ -504,6 +504,7 @@ Errors:
|
||||
AlreadyReady: Multifactor OTP (OneTimePassword) is already setup
|
||||
NotExisting: Multifactor OTP (OneTimePassword) doesn't exist
|
||||
InvalidCode: Invalid code
|
||||
Reused: This code was already used. Please wait for a new code
|
||||
NotReady: Multifactor OTP (OneTimePassword) isn't ready
|
||||
Locked: User is locked
|
||||
SomethingWentWrong: Something went wrong
|
||||
|
||||
@@ -487,6 +487,7 @@ Errors:
|
||||
AlreadyReady: El multifactor OTP (OneTimePassword) ya está configurado
|
||||
NotExisting: El multifactor OTP (OneTimePassword) no existe
|
||||
InvalidCode: Código no válido
|
||||
Reused: Este código ya se ha utilizado. Espera a que se genere un nuevo código
|
||||
NotReady: El multifactor OTP (OneTimePassword) no está listo
|
||||
Locked: El usuario está bloqueado
|
||||
SomethingWentWrong: Algo fue mal
|
||||
|
||||
@@ -505,6 +505,7 @@ Errors:
|
||||
AlreadyReady: L'OTP multifactoriel (Mot de passe à usage unique) est déjà configuré.
|
||||
NotExisting: OTP multifactoriel (Mot de passe à usage unique) n'existe pas.
|
||||
InvalidCode: Code invalide
|
||||
Reused: Ce code a déjà été utilisé. Veuillez attendre un nouveau code
|
||||
NotReady: Le système OTP multifactoriel (Mot de passe à usage unique) n'est pas prêt.
|
||||
Locked: L'utilisateur est verrouillé
|
||||
SomethingWentWrong: Il y a eu un problème
|
||||
|
||||
@@ -464,6 +464,7 @@ Errors:
|
||||
AlreadyReady: A többfaktoros OTP (OneTimePassword) már be van állítva
|
||||
NotExisting: A többfaktoros OTP (OneTimePassword) nem létezik
|
||||
InvalidCode: Érvénytelen kód
|
||||
Reused: Ezt a kódot már felhasználták. Kérjük, várjon egy új kódra
|
||||
NotReady: A többfaktoros OTP (OneTimePassword) nem áll készen
|
||||
Locked: A felhasználó zárolva van
|
||||
SomethingWentWrong: Valami elromlott
|
||||
|
||||
@@ -464,6 +464,7 @@ Errors:
|
||||
AlreadyReady: OTP multifaktor (OneTimePassword) sudah disiapkan
|
||||
NotExisting: OTP multifaktor (OneTimePassword) tidak ada
|
||||
InvalidCode: Kode tidak valid
|
||||
Reused: Kode ini sudah digunakan. Silakan tunggu kode baru
|
||||
NotReady: OTP multifaktor (OneTimePassword) belum siap
|
||||
Locked: Pengguna terkunci
|
||||
SomethingWentWrong: Ada yang tidak beres
|
||||
|
||||
@@ -505,6 +505,7 @@ Errors:
|
||||
AlreadyReady: Multifactor OTP (OneTimePassword) è già impostato
|
||||
NotExisting: Multifactor OTP (OneTimePassword) non esiste
|
||||
InvalidCode: Codice non valido
|
||||
Reused: Questo codice è già stato utilizzato. Attendere un nuovo codice
|
||||
NotReady: Multifattore OTP (OneTimePassword) non è pronto
|
||||
Locked: L'utente è bloccato
|
||||
SomethingWentWrong: Qualcosa è andato storto
|
||||
|
||||
@@ -505,6 +505,7 @@ Errors:
|
||||
AlreadyReady: 多要素OTP(ワンタイムパスワード)は設定済みです
|
||||
NotExisting: 多要素OTP(ワンタイムパスワード)が存在しません
|
||||
InvalidCode: 無効なコード
|
||||
Reused: このコードはすでに使用されています。新しいコードをお待ちください
|
||||
NotReady: 多要素OTP(ワンタイムパスワード)は利用可能でありません
|
||||
Locked: ユーザーはロックされています
|
||||
SomethingWentWrong: エラーが発生しました
|
||||
|
||||
@@ -504,6 +504,7 @@ Errors:
|
||||
AlreadyReady: 다중 인증 OTP(일회용 비밀번호)가 이미 설정되었습니다
|
||||
NotExisting: 다중 인증 OTP(일회용 비밀번호)가 존재하지 않습니다
|
||||
InvalidCode: 잘못된 코드입니다
|
||||
Reused: 이 코드는 이미 사용되었습니다. 새 코드를 기다려 주세요
|
||||
NotReady: 다중 인증 OTP(일회용 비밀번호)가 준비되지 않았습니다
|
||||
Locked: 사용자가 잠겼습니다
|
||||
SomethingWentWrong: 문제가 발생했습니다
|
||||
|
||||
@@ -505,6 +505,7 @@ Errors:
|
||||
AlreadyReady: Мултифактор OTP (Еднократна Лозинка) е веќе подесена
|
||||
NotExisting: Мултифактор OTP (Еднократна Лозинка) не постои
|
||||
InvalidCode: Невалиден код
|
||||
Reused: Овој код е веќе искористен. Ве молиме почекајте нов код
|
||||
NotReady: Мултифактор OTP (Еднократна Лозинка) не е подготвена
|
||||
Locked: Корисникот е заклучен
|
||||
SomethingWentWrong: Се случи нешто неочекувано
|
||||
|
||||
@@ -504,6 +504,7 @@ Errors:
|
||||
AlreadyReady: Multifactor OTP (OneTimePassword) is al ingesteld
|
||||
NotExisting: Multifactor OTP (OneTimePassword) bestaat niet
|
||||
InvalidCode: Ongeldige code
|
||||
Reused: Deze code is al gebruikt. Wacht op een nieuwe code
|
||||
NotReady: Multifactor OTP (OneTimePassword) is niet klaar
|
||||
Locked: Gebruiker is vergrendeld
|
||||
SomethingWentWrong: Er is iets misgegaan
|
||||
|
||||
@@ -505,6 +505,7 @@ Errors:
|
||||
AlreadyReady: Wieloskładnikowe OTP (jednorazowe hasło) jest już skonfigurowane
|
||||
NotExisting: Wieloskładnikowe OTP (jednorazowe hasło) nie istnieje
|
||||
InvalidCode: Nieprawidłowy kod
|
||||
Reused: Ten kod został już użyty. Poczekaj na nowy kod
|
||||
NotReady: Wieloskładnikowe OTP (jednorazowe hasło) nie jest gotowe
|
||||
Locked: Użytkownik jest zablokowany
|
||||
SomethingWentWrong: Coś poszło nie tak
|
||||
|
||||
@@ -501,6 +501,7 @@ Errors:
|
||||
AlreadyReady: A autenticação de vários fatores por OTP (senha única) já está configurada
|
||||
NotExisting: A autenticação de vários fatores por OTP (senha única) não existe
|
||||
InvalidCode: Código inválido
|
||||
Reused: Este código já foi utilizado. Aguarde um novo código
|
||||
NotReady: A autenticação de vários fatores por OTP (senha única) não está pronta
|
||||
Locked: O usuário está bloqueado
|
||||
SomethingWentWrong: Algo deu errado
|
||||
|
||||
@@ -504,6 +504,7 @@ Errors:
|
||||
AlreadyReady: MFA OTP (OneTimePassword) este deja configurat
|
||||
NotExisting: Multifactor OTP (OneTimePassword) nu există
|
||||
InvalidCode: Cod nevalid
|
||||
Reused: Acest cod a fost deja utilizat. Vă rugăm să așteptați un cod nou
|
||||
NotReady: Multifactor OTP (OneTimePassword) nu este gata
|
||||
Locked: Utilizatorul este blocat
|
||||
SomethingWentWrong: Ceva nu a mers bine
|
||||
|
||||
@@ -505,6 +505,7 @@ Errors:
|
||||
AlreadyReady: OTP (OneTimePassword) уже настроен
|
||||
NotExisting: OTP (OneTimePassword) не существует
|
||||
InvalidCode: Неверный код
|
||||
Reused: Этот код уже был использован. Пожалуйста, дождитесь нового кода
|
||||
NotReady: OTP (OneTimePassword) не готов
|
||||
Locked: Пользователь заблокирован
|
||||
SomethingWentWrong: Что-то пошло не так
|
||||
|
||||
@@ -504,6 +504,7 @@ Errors:
|
||||
AlreadyReady: Tvåfaktor OTP (OneTimePassword) är redan konfigurerat
|
||||
NotExisting: Tvåfaktor OTP (OneTimePassword) finns inte
|
||||
InvalidCode: Ogiltig kod
|
||||
Reused: Den här koden har redan använts. Vänta på en ny kod
|
||||
NotReady: Tvåfaktor OTP (OneTimePassword) är inte redo
|
||||
Locked: Användaren är spärrad
|
||||
SomethingWentWrong: Någonting gick fel
|
||||
|
||||
@@ -504,6 +504,7 @@ Errors:
|
||||
AlreadyReady: Çok faktörlü OTP (Tek Seferlik Şifre) zaten ayarlanmış
|
||||
NotExisting: Çok faktörlü OTP (Tek Seferlik Şifre) mevcut değil
|
||||
InvalidCode: Geçersiz kod
|
||||
Reused: Bu kod zaten kullanıldı. Lütfen yeni bir kod bekleyin
|
||||
NotReady: Çok faktörlü OTP (Tek Seferlik Şifre) hazır değil
|
||||
Locked: Kullanıcı kilitli
|
||||
SomethingWentWrong: Bir şeyler yanlış gitti
|
||||
|
||||
@@ -505,6 +505,7 @@ Errors:
|
||||
AlreadyReady: Багатофакторний OTP (одноразовий пароль) уже налаштовано
|
||||
NotExisting: Багатофакторний OTP (одноразовий пароль) не існує
|
||||
InvalidCode: Недійсний код
|
||||
Reused: Цей код вже було використано. Будь ласка, зачекайте на новий код
|
||||
NotReady: Багатофакторний OTP (одноразовий пароль) не готовий
|
||||
Locked: Користувача заблоковано
|
||||
SomethingWentWrong: Щось пішло не так
|
||||
|
||||
@@ -504,6 +504,7 @@ Errors:
|
||||
AlreadyReady: OTP (一次性密码) 已设置
|
||||
NotExisting: OTP (一次性密码) 不存在
|
||||
InvalidCode: 无效的验证码
|
||||
Reused: 此验证码已被使用。请等待新的验证码
|
||||
NotReady: OTP (一次性密码) 还没准备好
|
||||
Locked: 用户被锁定
|
||||
SomethingWentWrong: 似乎出问题了
|
||||
|
||||
Reference in New Issue
Block a user