Nanoid CVE-2026-73086: one size value can make tokens predictable
A 32-bit integer wrap corrupts the shared CSPRNG pool offset. Later IDs can become a constant string, so updating alone may not complete the response.
- AUTHOR
- Karol Rapacz / Breachroad CEO · OSCP · PNPT
- PUBLISHED
- 11 August 2026
- READING TIME
- 12 min read
- TOPIC
- Supply Chain Security
CVE-2026-73086, published on 11 August 2026, affects Nanoid, a popular JavaScript generator for compact identifiers. Passing a sufficiently large value to nanoid(size) can coerce it into a negative 32-bit integer and corrupt the global random-pool offset. The consequence extends beyond one failed call: subsequent identifiers in the same process can become the deterministic string uuuuuuuuuuuuuuuuuuuuu until restart.
Affected releases are below 3.3.12 and from 4.0.0 up to, but not including, 5.1.11. The project advisory scores it 7.4 under CVSS 3.1. An attacker must influence size; a default call with no user-controlled parameter does not trigger the flaw. Once triggered, however, it can affect other, apparently unrelated requests served by the same process.
What happens to the random pool
For performance, Nanoid keeps a process-wide pool of bytes obtained from the operating system’s cryptographic random generator. Each identifier consumes part of the pool, with poolOffset tracking the current position. This is a normal optimisation as long as offset arithmetic stays within valid bounds.
In affected code, size could be reduced to a signed 32-bit integer. The value 2147483648, or 2^31, becomes -2147483648 after coercion. Execution enters fillPool() with a value outside the intended domain. The global offset moves into an invalid state, and later reads no longer consume the expected fresh random bytes.
The generator then returns a repeated value made of u characters. This is not a mathematical break of the operating system CSPRNG. Its random source remains strong; buffer management and index arithmetic are broken. The distinction matters: the fix is input validation and corrected state logic, not replacing the system random generator.
When an identifier is a secret
Nanoid is used for database keys, public slugs, correlation IDs and cache names. Predictability there may cause collisions or availability issues without account takeover. Elsewhere the same generator creates password-reset tokens, invitation secrets, session IDs, API keys or CSRF tokens. Loss of unpredictability then becomes a security failure.
Inventory must answer two questions: can an attacker control size, and do later Nanoid values provide authentication or authorization? Merely finding the package in package-lock.json does not establish reachability. Conversely, a deeply nested dependency is not low risk if a framework uses it for sessions.
The most concerning scenario crosses request boundaries. One request with a huge size poisons the process, and a token issued later to someone else is constant. Monitoring only the attacker’s response can miss the impact. Teams need to correlate the event with identifiers generated until restart or remediation.
What was fixed
The project released fixes in Nanoid 3.3.12 and Nanoid 5.1.11. Organisations remaining on supported 3.x can move to 3.3.12, while 5.x users need at least 5.1.11. The 4.x line is also vulnerable, so migration should target a current fixed branch while accounting for compatibility changes.
Fixed packages appeared before the public CVE record, a common coordinated-disclosure pattern. A scanner may start alerting on 11 August even though an upgrade was already possible. SBOM monitoring should therefore include project advisories rather than rely solely on a monthly NVD export.
Do not reproduce the validation as an arbitrary limit in one controller. The public library boundary must reject invalid sizes before arithmetic changes shared state. The parameter should be a finite positive integer within a documented range. An application wrapper can impose an even smaller business-specific maximum.
Response and impact containment
- Find Nanoid in production images and bundles, then determine the effective resolved version.
- Identify calls with a
sizeargument, especially values sourced from HTTP, tenant configuration, files or queue messages. - Establish where output serves as a secret, one-time token, session ID or CSRF value.
- Upgrade to
3.3.12,5.1.11or a later safe release on the appropriate branch and rebuild every image. - Terminate old processes after deployment. Hot code reload may not reset corrupted module state in every runtime.
- Search records for the deterministic string and unusual duplicates, while avoiding logging complete valid secrets.
- Invalidate or rotate security values issued after a plausible trigger if their randomness cannot be established.
The update–restart–rotation sequence matters. Restarting before changing the library repairs state only until another malicious call. Issuing new tokens before removing the vulnerability can create another predictable series. Close the input, start a clean process and only then issue replacement secrets.
In a multi-instance service, determine which pod or worker served the trigger. Pool state is process-local, so one replica may produce bad values while others remain healthy. Instance identifiers in telemetry help narrow the window, although load balancing and automatic restarts can complicate reconstruction.
Detection and regression testing
The most precise signal is the known constant identifier or a sudden increase in collisions. Alert on repeated output in a short window without retaining full values: a protected HMAC or uniqueness metric can be enough. For password-reset tokens, the alert should halt issuance rather than create a low-priority ticket.
A regression test should submit an invalid size, expect a controlled exception, and then verify that ordinary identifiers retain correct format and uniqueness. Test boundaries around 2^31, negative values, NaN, infinity, strings and very large numbers. The property being tested is that invalid input cannot alter global state.
At design level, a cryptographic function should reject an invalid parameter atomically. It must not mutate a shared buffer and only then report failure. Security state should not depend on the history of failed calls made by different users.
Facts and conclusions
The affected ranges, constant output, trigger value and fixed releases come from the project advisory and CVE record. The sources do not report a mass campaign. Breachroad’s conclusion is that response includes reviewing tokens issued after a trigger because an update does not invalidate values already stored or delivered.
Closure evidence should cover every production image, terminated old processes, a passing boundary regression test and expiry or revocation of tokens from the suspect window.
Nanoid is a good teaching case because dependency security is not just changing version numbers. Teams must understand reachability, output purpose and process state. We build those skills in cybersecurity training for organisations. An independent web and API penetration test can further examine dependency handling, sessions and token design.


