How should a service issue, store and consume the backup codes handed out alongside an authenticator enrolment?
answer
- a second credential, not a hint
- the server never reproduces one
- single use, marked rather than deleted
- regenerate the set, never top up
- the remaining count is your inventory
basics
~20 sIssue them as a set, once, at activation; store each one-way, because the server only ever checks a typed one and never reproduces it; mark each used when consumed; and regenerate the whole set rather than topping it up.
solid answer
~50 sA backup code is not a hint or a reminder - it is a **parallel credential** that satisfies the same second factor as the authenticator, so it deserves the same care. Issue them as a set at activation, display or download them exactly once, and draw them from a cryptographically secure generator with enough length that guessing one is no cheaper than guessing a code. Store them **one-way**, unlike the shared secret: the server never has to reproduce a backup code, only to check one that was typed, so there is no reason to keep a recoverable copy. Mark a code used when it is consumed rather than deleting the row, so the remaining count and the times they were spent stay visible. And regenerate wholesale: a new set invalidates every unused code from the old one, because that is the only action that makes a previously printed sheet dead.
go deeper
Know what they are for and the two rules that matter: each code works once, and the set is shown to the holder only when it is issued.
Explain the storage contrast. Backup codes are stored one-way because the server only compares a typed one, while the authenticator's shared secret must stay recoverable because the server recomputes from it.
Argue the lifecycle. Say why consumption is marked rather than deleted, what an unexplained drop in the count is evidence of, and why regeneration must replace the set wholesale rather than extend it.
Treat the set as the real second factor for the tail of your population, and price it: how often holders fall back to it, what a leaked sheet costs, and where the line sits between this and an account-recovery process.
## A backup code is a second credential, not a hint When a holder of a movement licence activates an authenticator, the service hands them a short list of codes to keep somewhere safe - in a wallet, taped inside a shed office door, photographed and forgotten in a gallery. Anyone holding one of those can complete the second factor without the handset. That makes the printed sheet exactly as strong as the factor it substitutes for, and everything below follows from taking that seriously. ## Issue: a set, once, at activation - Generate the whole set at once, from a cryptographically secure generator, at the moment the enrolment is confirmed. Issuing them before confirmation is meaningless, because there may never be an enrolment for them to back up. - Make each code long enough that guessing one is no cheaper than guessing a time-derived code. A backup code has no 30-second expiry working for it, so the length is the only thing bounding a guessing campaign against the set. - Display or offer them for download in that one response, formatted to be written down or printed, and say plainly that they will not be shown again. - Show the **remaining count** afterwards, and prompt the holder to regenerate when it gets low. The count is the only inventory anyone has. ## Store: one-way, and why that differs from the shared secret This is where candidates trip, because the same account holds two secrets stored by opposite rules. | | authenticator shared secret | backup code | |---|---|---| | must the server reproduce it | yes, on every verification | no - only compare one typed in | | stored form | recoverable ciphertext | one-way derivation, never in clear | | how many exist | one per enrolment | a set, each independently usable | | how it ends | superseded by a new enrolment | consumed once, or killed by regeneration | The shared secret has to be recoverable because verification recomputes an expected code from it. A backup code is different: the holder types the whole value, so the server derives from the submission and compares against a stored derivation, exactly as it does for a password. Nothing ever needs the original back. Storing them in clear - so a support desk can read one out, which is the usual excuse - turns the credentials table into a list of working second factors and puts the support desk inside the trust boundary. ## Consume: mark it used, do not delete it A backup code is single-use: once spent, it never verifies again, regardless of how much of the set remains. Mark the row **used**, with the time, rather than deleting it. The reasons are all operational: 1. The remaining count stays truthful without recounting anything. 2. The holder can be shown when each code was spent, which is how a leaked sheet is noticed at all. 3. An unexplained drop is a signal. Nobody remembers spending three codes last month; a sheet that quietly emptied is the evidence that someone else has it. The response to that signal is not to investigate one code. It is to regenerate the set, because once one code has been spent by the wrong person the whole printing is suspect. ## Regenerate wholesale, never top up The most common design mistake is letting a holder who is running low add a few more codes to the existing set. It is friendlier and it is wrong: - It leaves the old printing alive indefinitely, with no point at which those values stop working. - The count stops meaning anything, because the set no longer has one issue point to count from. - Nobody can say which printing a given code came from, so a suspected leak has no clean remedy short of removing the factor entirely. Regeneration is the one action that makes every previously issued code dead at a stroke. Treat it as the only way to change the set: a new set replaces the old one whole, is displayed once like the first, and resets the count. It is also the right response to a lost wallet, a shared-out password manager, or a holder who is simply not sure where the sheet went. ## Where this stops All of the above assumes the holder still has *something*. What to do when both the authenticator and the backup codes are gone is an account-recovery decision rather than a second-factor one, and it is where the real strength of the account gets set: a recovery path has to be at least as hard to abuse as the factor it replaces, or the factor is decorative. Design that deliberately rather than leaving it to whatever a support desk can be talked into.
- Why regenerate the whole set instead of topping it up?Because wholesale regeneration is the only action that makes every previously printed value dead at once, and the remaining count is only meaningful if the set has a single issue point. A topped-up set leaves an unaccounted-for sheet alive indefinitely, and nobody can say afterwards which printing a given code came from.
- What does an unexplained drop in remaining backup codes tell you?That the printed set has probably leaked - which is why consumption is marked rather than silently deleted, and why the holder can see the count and when each code was spent. The response is to regenerate the whole set rather than to investigate one code, because once one has been spent by the wrong person the whole printing is suspect.
- What happens when both the authenticator and the backup codes are gone?That is an account-recovery decision rather than a second-factor one, and it is where the account's real strength is set: the recovery path has to be at least as hard to abuse as the factor it replaces, or the factor is decorative. Design it deliberately instead of leaving it to whatever the support desk can be talked into.
saying these in an interview costs you the question
- Stores backup codes in clear so a support desk can read one out
- Lets a holder add a few more codes when the set runs low
- Treats a backup code as weaker than an authenticator code
- Deletes a consumed code instead of marking it used
- Shows the set again on demand from account settings