Many security features depend on a value that an attacker must not be able to guess. Password-reset links, email-verification links, invitation codes, session identifiers, and one-time capability URLs are common examples. If such a token is predictable, an attacker may not need to steal it. They may be able to guess a valid value instead.
The defensive requirement is therefore stronger than “make the token look random.” A security token needs enough entropy, meaning uncertainty from the attacker’s point of view, and it needs to come from a generator designed for security-sensitive randomness.
The practical mental model is:
security token = unpredictable random value + controlled lifetime and useThis article explains what unpredictability means, why ordinary pseudo-random generators are the wrong tool, how token encoding relates to entropy, and what additional controls are still needed after generation.
Start with the attacker’s question
Suppose an application creates a password-reset record and sends the user a link containing a token. The server accepts the token as proof that the caller possesses the reset link.
A simplified design is:
random token -> reset record -> link sent to userThe attacker does not need to know how the link was delivered if they can predict the token. Their relevant question is: how hard is it to produce a value that the server currently accepts?
That is why identifiers based on timestamps, sequential counters, usernames, database IDs, or ordinary application random-number generators are poor choices for bearer-style security tokens. A value can be unique without being unpredictable. A timestamp may never repeat, for example, while still revealing most or all of the information needed to guess nearby values.
The threat model here is token guessing or prediction by someone who does not already possess the valid token. Strong random generation reduces that risk. It does not protect a token that is leaked through logs, browser history, analytics, malware, an insecure transport path, or a compromised endpoint.
Use a cryptographically secure random generator
General-purpose pseudo-random number generators are often designed for simulation, sampling, games, or other non-security work. Their output may be statistically useful while still allowing future values to be inferred when enough internal state or output is known.
A cryptographically secure pseudorandom number generator, usually shortened to CSPRNG, is designed so that its output is suitable for security uses under its stated assumptions. Application developers should normally use the operating system’s secure randomness through a standard library or framework API rather than designing a generator themselves.
Conceptually, token generation should look like this:
random_bytes = secure_random_bytes(32)
token = url_safe_encode(random_bytes)The important operation is secure_random_bytes(32). The encoding only turns those bytes into text that is convenient to transport. It does not create the randomness.
For a new application-generated bearer token, 32 random bytes provide 256 bits of raw randomness when all bytes come from a suitable CSPRNG. This is a conservative, practical size for many token uses. Existing frameworks and protocols may define their own token formats and requirements; use those mechanisms instead of replacing them with a custom format merely to follow a particular byte count.
Distinguish entropy from text length
A long string is not automatically a strong token.
Consider these two simplified values:
20260903235800000000000000000000
m4Y...randomly encoded bytes...QwThey may have similar visible lengths, but the first value contains a large predictable component. Its character count therefore says little about how many guesses an attacker must consider.
If 32 bytes are generated independently by a suitable CSPRNG, the source contains 256 bits of randomness before encoding. Hexadecimal representation would use 64 characters because each byte becomes two hexadecimal digits. Base64-style encodings represent the same bytes more compactly. Changing the encoding changes the text length, not the randomness already present in the bytes.
This distinction prevents a common design error: increasing token length while continuing to derive the value from predictable inputs.
Keep meaning outside the random token
A random bearer token is easiest to reason about when the token itself is opaque. An opaque token has no application meaning that the holder needs to interpret; the server uses it to locate or validate server-side state.
For example, the server-side record can contain:
token reference
account ID
purpose: password reset
created time
expiry time
used: falseThe random token does not need to contain the account ID, email address, privilege level, or expiration time. Keeping those decisions on the server reduces accidental information exposure and makes revocation or one-time-use rules straightforward.
Signed or encrypted structured tokens are valid designs in some protocols, but they solve a different problem and introduce additional key-management and validation requirements. Do not choose a structured token merely because it can carry data when a random opaque reference is sufficient.
Store tokens so a database read is not automatically possession
Some tokens are short-lived enough that teams store them directly. A stronger design for many reset, verification, and invitation tokens stores a one-way digest of the token instead of the bearer value itself.
The flow is:
generate random token
|
+--> send raw token to the intended user
|
+--> hash token -> store digestWhen the token returns, the server hashes the presented value and looks up or compares the digest. Because a high-entropy random token is not a human-chosen password, a fast cryptographic hash can be appropriate for this lookup pattern; password-specific slow hashing is intended to address the different problem of low-entropy password guessing.
Hashing stored tokens can reduce the usefulness of a database-only disclosure because the raw bearer values are not present there. It does not help if the attacker can observe tokens before hashing, read application memory, control the application process, or intercept the token elsewhere.
If the application compares secret-derived values directly, use the comparison mechanism recommended by the relevant platform or cryptographic API rather than inventing custom comparison logic.
Bind every token to a purpose and lifetime
Randomness answers “can an outsider guess this value?” It does not answer “what may this value do?”
A token should be accepted only for the operation for which it was created. A password-reset token should not become an email-verification token merely because both records use the same token table. Store and verify the intended purpose explicitly.
Tokens should also have bounded lifetimes appropriate to their use. Shorter validity reduces the period during which a leaked token remains useful, but excessively short expiry can cause failed user flows and repeated token issuance. The right duration depends on delivery delay, user expectations, operation sensitivity, and recovery options.
For one-time operations, mark the token consumed atomically with the protected state change. If two requests can both validate an unused token before either marks it used, the application may accidentally permit reuse despite having a used flag.
A useful acceptance sequence is:
receive token
-> derive lookup value
-> find matching record
-> verify purpose
-> verify not expired
-> verify not already used
-> perform authorized operation and consume token atomicallyRate controls can further reduce online guessing pressure, especially when a token format has unavoidable constraints. They are defense in depth, not a substitute for sufficient randomness.
Avoid weakening randomness during formatting
Token generation sometimes starts securely and becomes weaker during transformation.
One mistake is truncating a secure value to fit an unnecessarily small database column or user-interface field. Another is generating random bytes and then mapping them into a small character set with a biased algorithm. A third is combining secure random output with predictable values and then assuming the result has entropy equal to its total length.
Prefer a standard token API that directly produces enough secure random bytes and a standard encoding appropriate for the transport channel. If a token must fit a strict human-entry format, calculate the actual search space and account for online guessing controls rather than assuming that a certain number of characters is sufficient.
Human-entered short codes are a distinct design case. Usability may require fewer characters than a high-entropy bearer URL. Such codes generally need tighter attempt limits, shorter lifetimes, binding to a specific transaction or account context, and careful recovery behavior.
Verify the property that matters
A test that generates one thousand tokens and checks that they are different is useful for catching obvious implementation failures, but it does not prove cryptographic unpredictability. Many predictable sequences are perfectly unique.
Verification should focus on the construction and its boundaries:
- confirm the application calls a documented cryptographic randomness API rather than a simulation-oriented generator;
- confirm the requested random-byte count is explicit where the API’s default could change or is not part of a stable contract;
- confirm storage and transport do not truncate or normalize the token;
- confirm expired, consumed, wrong-purpose, and unknown tokens are rejected;
- confirm one-time consumption remains correct under concurrent requests;
- confirm raw tokens are not intentionally written to application logs, analytics events, or error reports.
Statistical tests can detect some broken generators, but application-level statistical testing cannot establish that a generator is cryptographically secure. That property should come from using a well-reviewed platform primitive with documented security intent.
Know what random tokens do not solve
A strong random token reduces guessing and prediction risk. It does not make possession trustworthy after the token has leaked.
For sensitive flows, consider the full path: generation, storage, delivery, browser or client handling, validation, expiration, revocation, logging, and recovery. Transport encryption helps protect tokens in transit. Careful logging reduces accidental copies. Purpose binding limits cross-flow reuse. Expiration and one-time consumption limit the useful window. Authentication or additional confirmation may be justified for especially consequential actions.
The correct defense therefore depends on the token’s authority. A low-impact email-verification link may need a simpler control set than a token that can change account ownership or authorize a high-value operation.
Conclusion
Treat a security token as a temporary capability: anyone who presents a valid bearer value may be able to exercise the authority attached to it. The token must therefore be difficult to guess, narrowly scoped, and useful only for an appropriate period.
Generate tokens from a standard cryptographically secure random source, reason about entropy rather than visible string length, keep application meaning in controlled server-side state when an opaque token is sufficient, and avoid leaking the raw bearer value through storage or observability systems. Then add purpose checks, expiry, one-time consumption, and rate controls according to the operation’s threat model.
The central rule is simple: uniqueness identifies a value; cryptographic unpredictability protects a security token from being guessed.