You have to specify a password-reset token: the number of bits behind it, and how those bits are turned into the characters that appear in the emailed link. Explain how you size the value, why the character alphabet changes the answer, and what goes wrong when random bytes are mapped into an alphabet by simple remainder arithmetic.
answer
- 128 bits, then encode
- hex 4 / base32 5 / base64 6 bits per char
- birthday: collisions near 2^(n/2)
- 256 mod 62 = 8 → biased first symbols
- rejection sampling or power-of-two alphabet
basics
~20 sSize by entropy bits, not by string length: aim for 128 bits of generator output. Encoding only changes how many characters carry those bits — hex gives 4 per character, base64 gives 6. Mapping bytes with a plain remainder skews the alphabet; use rejection sampling.
solid answer
~50 s**Size in bits.** 128 bits of generator output is the working standard for bearer values. Two separate bounds justify it: online guessing (each attempt succeeds with probability 2^-n, so length plus rate limiting makes it hopeless), and collisions, where the birthday bound says you expect a collision around 2^(n/2) issued values — a 64-bit token collides after roughly four billion issuances, which is not a comfortable margin for a high-volume system. **Then encode.** The number of characters is a presentation detail: hex carries 4 bits per character, base32 carries 5, base64/base64url carries 6. A 32-character hex string is 128 bits; a 32-character string drawn from a 10-digit alphabet is about 106. Never judge strength by string length. **Avoid modulo bias.** Mapping a uniform byte into an alphabet of 62 with a remainder favours the first 8 symbols, because 256 is not a multiple of 62. Use rejection sampling or draw more bits than needed. Small effect, but a reliable signal of an ad-hoc implementation.
go deeper
Give the practical rule: at least 128 bits from a cryptographic generator, encoded as hex or base64url, and never build the token out of ids, timestamps or hashes of them.
Show the arithmetic — bits per character per alphabet, and why a 32-character hex string is 128 bits while a 32-character digit string is not. Explain modulo bias with the 256-versus-62 example and name rejection sampling.
Add the two bounds explicitly (online guessing and the birthday collision bound) and the compensating-control argument for deliberately short codes; connect the entropy budget to the rest of the token lifecycle: single use, expiry, invalidation, no logging.
Frame it as a policy: one vetted token-minting facility with the entropy budget and encoding fixed in one place, so no feature team ever re-derives these numbers or hand-rolls an alphabet mapping.
## Step one: decide the entropy, not the length The security of a bearer value — a reset token, a session identifier, an invite code, an API key — is measured in bits of unpredictable generator output behind it. Everything after that is packaging. Two distinct bounds set the number. **Guessing resistance.** An attacker who can send requests must, per attempt, hit a 2^-n chance. At 128 bits this is beyond hopeless even with unlimited requests, which is why 128 is the default answer. Lower values can be defensible when the value is short-lived and rate-limited — a six-digit one-time code is about 20 bits and survives only because it expires in minutes and dies after a few wrong attempts — but then the compensating controls are *part of the design*, not an afterthought, and their absence is the vulnerability. **Collision resistance.** Because tokens are usually looked up by value, two users holding the same token is a correctness and security failure. The birthday bound says a collision becomes likely after about 2^(n/2) values: 2^32 (four billion) for a 64-bit token, 2^64 for a 128-bit token. Since guessing already demanded a large *n*, collisions are usually solved for free — but the reasoning matters when someone proposes shortening the token for usability. ## Step two: encode without lying to yourself Encoding maps bits to characters at a fixed rate: - hexadecimal: 16 symbols, **4 bits per character** - base32: 32 symbols, **5 bits per character** - base64 / base64url: 64 symbols, **6 bits per character** - decimal digits: 10 symbols, **~3.32 bits per character** - mixed-case alphanumeric (62 symbols): **~5.95 bits per character** So 128 bits is 32 hex characters, 26 base32 characters, or 22 base64url characters. Conversely a 20-character alphanumeric string carries about 119 bits *if* every character was independently drawn — and far less if it was produced by some structured scheme. The number that matters is the entropy at the source; the character count is a consequence. Two related traps. First, **encoding is not entropy**: encoding an 8-byte value as a 32-character hex string does not make it a 128-bit token. Second, **hashing is not entropy**: hashing a user id, a timestamp, or a database sequence value produces a long random-looking string whose real unpredictability equals the attacker's uncertainty about the input, which is typically none. ## Step three: mapping bytes into an alphabet The naive construction takes a uniform random byte in 0–255 and reduces it modulo the alphabet size. When the alphabet size does not divide 256, the mapping is not uniform: for a 62-symbol alphabet, 256 = 4×62 + 8, so the first 8 symbols each have five chances of being produced and the rest have four. That is roughly a 25% relative excess on those symbols. The practical impact is small — it shaves a fraction of a bit per character — but it matters for two reasons. It is a *free* correctness loss with a well-known fix, and it is a strong indicator that the token construction was written from scratch rather than taken from a vetted routine, which usually means other, larger mistakes are nearby. The standard fixes: - **Rejection sampling**: draw a byte, discard it if it falls in the non-uniform tail (here, values ≥ 248), otherwise reduce modulo 62. Uniform, at the cost of occasionally drawing again. - **Choose a power-of-two alphabet**: with 16, 32 or 64 symbols the mapping is a pure bit-slice and no bias exists. This is why hex, base32 and base64url are the pragmatic answers. - **Draw far more bits than needed** before reducing, which shrinks the bias exponentially without eliminating it — acceptable, but why bother when a power-of-two alphabet is free. ## Constructions to reject outright - Sequential or timestamp-prefixed identifiers used as bearer values — unique but enumerable. - Time-and-hardware-derived identifier formats, which encode a clock and a machine identifier rather than unpredictable bits. - Hashing anything predictable and treating the digest length as the strength. - Truncating a strong value for prettiness without recomputing the guessing and collision bounds. - Reusing a value that is also displayed elsewhere as a public identifier: a value cannot simultaneously be a public key for a record and a secret proving access to it. ## Adjacent design points The entropy budget is only one half of a reset token's design. The rest — single use, short expiry, invalidation on use and on password change, constant-time lookup, and never placing the value where it will be logged — is what turns a strong random value into a safe workflow. In an interview it is worth naming these in one sentence to show that you know the token's strength is necessary rather than sufficient. ## How to answer Give the number and the reason: 128 bits from a cryptographic generator, justified by online guessing and the birthday bound; encode with a power-of-two alphabet so the bits-per-character arithmetic is exact and no modulo bias arises; never reason about strength from string length; and if a non-power-of-two alphabet is required for usability, reject-sample.
- A product manager wants a six-digit reset code sent by SMS instead of a long link. Can that be secure?Six digits is about 20 bits, which cannot resist guessing on its own, so the security has to come from the surrounding controls: a short expiry measured in minutes, single use, a hard attempt limit per code and per account, throttling by source, and invalidation of prior codes on reissue. Those controls become load-bearing security requirements rather than nice-to-haves, and the design must state what happens when the limit is reached.
- Someone proposes deriving the token as a hash of the user id plus the request timestamp, arguing the digest is 256 bits long. What is wrong?The digest length is not the entropy. A hash is a deterministic public function, so the unpredictability of the output equals the attacker's uncertainty about the input — here a known user id and a timestamp within a small window, perhaps a few thousand candidates. The attacker enumerates them offline and produces the valid token. Strength must come from generator output, not from digest width.
saying these in an interview costs you the question
- Judging token strength by character count without stating the alphabet.
- "We hash it, so it is 256 bits of entropy" — the digest inherits the input's guessability.
- Padding or encoding a short random value and calling the encoded length the strength.
- Using a timestamp-and-machine-derived identifier format as a secret bearer value.
- Ignoring modulo bias as purely theoretical while hand-rolling the token routine anyway.