Skip to content

fix: generate session IDs from 128 cryptographically random bits - #222

Merged
sadjow merged 3 commits into
mainfrom
fix/session-id-entropy
Oct 14, 2026
Merged

sadjow merged 3 commits into
mainfrom
fix/session-id-entropy

Conversation

@sadjow

@sadjow sadjow commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

Session IDs came from Math.random().toString(36).substring(2, 15). That generator is not designed for identifiers: it carries at most about 52 bits of state per call, is predictable from earlier outputs, and the string drops trailing zero digits, so IDs vary in length (the new test caught an 11 character one on its first run). Inside the module the ID only separates sessions within one browser, but getAttribution() payloads exist to be sent to a backend or CRM, where sessions from many visitors are compared and a collision would merge them.

generateSessionId now encodes 16 bytes from crypto.getRandomValues as 32 lowercase hex characters. crypto.randomUUID would be shorter to write, but browsers only expose it in secure contexts, and sites served over plain HTTP should keep tracking. Collection only runs in the browser, and crypto.getRandomValues is available in every browser Nuxt supports.

Existing sessions keep their stored ID until the tab session ends, so deduplication within a session is unaffected. New IDs are longer than before; nothing in the module depends on the length, but backends that validate the old format would need to accept 32 hex characters.

test/session-id.test.ts generates 100 IDs and checks that each is 32 hex characters and that all are distinct. Checking many IDs makes it fail reliably when a byte loses its leading zero or the generator stops being random, which a single sample would miss most of the time. It failed on the old generator (expected '45s5g9vhehe' to match /^[0-9a-f]{32}$/) and passes now.

Validation: pnpm lint --max-warnings 0, Prettier on the changed files, pnpm test:types, pnpm dev:build, pnpm test (82 tests), and pnpm test:package pass.

@sadjow

sadjow commented Oct 14, 2026

Copy link
Copy Markdown
Member Author

Review notes before merging:

  • getSessionID only runs in the browser, inside capture, and MDN confirms getRandomValues is the one Crypto member available outside secure contexts, so sites on plain HTTP keep tracking. IDs already in sessionStorage keep working because validation only requires a string and deduplication compares IDs for equality.
  • The test checked a single ID, which would still pass about a third of the time if padStart were dropped, because an ID has no byte below 0x10 with probability (15/16)^16. d0cc40f checks 100 IDs for format and uniqueness, and dropping padStart or returning a constant now fails every run.
  • This and feat: capture GA4 campaign parameters and Google click IDs used on iOS and Display & Video 360 #223 both add lines above generateSessionId in src/runtime/utm.ts, so whichever merges second needs a small conflict resolution.

@sadjow
sadjow enabled auto-merge (squash) October 14, 2026 01:20
@sadjow
sadjow merged commit 7143b39 into main Oct 14, 2026
7 checks passed
@sadjow
sadjow deleted the fix/session-id-entropy branch October 14, 2026 01:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant