[{"data":1,"prerenderedAt":1216},["ShallowReactive",2],{"guide-csprng-security-guide":3},{"id":4,"title":5,"body":6,"date":1208,"description":1209,"extension":1210,"meta":1211,"navigation":152,"path":1212,"readingTime":156,"seo":1213,"stem":1214,"__hash__":1215},"guides\u002Fguides\u002Fcsprng-security-guide.md","Cryptographic Random Number Guide",{"type":7,"value":8,"toc":1201},"minimark",[9,21,26,31,34,66,166,172,176,183,324,330,415,425,429,435,527,533,694,700,782,789,951,955,962,1073,1087,1091,1180,1189,1197],[10,11,12,13,17,18,20],"p",{},"Not all random numbers are created equal. ",[14,15,16],"code",{},"Math.random()"," produces pseudorandom values suitable for animations and games, but completely unsuitable for security-sensitive operations like token generation, session IDs, or password reset links. The difference is not academic — attackers have exploited predictable random number generators to hijack accounts, bypass authentication, and extract encrypted data. This guide explains why ",[14,19,16],{}," fails for security and how to use the Web Crypto API correctly.",[22,23,25],"h2",{"id":24},"why-mathrandom-is-unsafe","Why Math.random() Is Unsafe",[10,27,28,30],{},[14,29,16],{}," uses a deterministic pseudorandom number generator (PRNG), typically an xorshift128+ variant. It produces a sequence that appears random but is entirely determined by its internal state. If an attacker can observe enough outputs, they can reconstruct the state and predict all future — and past — values.",[10,32,33],{},"The attacks are practical:",[35,36,37,48,57],"ul",{},[38,39,40,44,45,47],"li",{},[41,42,43],"strong",{},"State recovery",": Observing roughly 160 bits of ",[14,46,16],{}," output (about 5 consecutive values) is enough to reconstruct the internal state in V8's implementation.",[38,49,50,53,54,56],{},[41,51,52],{},"Cross-tab leakage",": All tabs in the same browser process share the same PRNG state. A malicious website in one tab can observe ",[14,55,16],{}," calls and correlate them with token generation in another tab.",[38,58,59,62,63,65],{},[41,60,61],{},"Node.js server-side",": On the server, ",[14,64,16],{}," state is shared across all requests. One compromised request can predict tokens for all other concurrent sessions.",[67,68,73],"pre",{"className":69,"code":70,"language":71,"meta":72,"style":72},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F UNSAFE — predictable token\nfunction generateToken() {\n  return Math.random().toString(36).slice(2)  \u002F\u002F Predictable!\n}\n\n\u002F\u002F If attacker observes output \"k0a1b2c3d\", they can\n\u002F\u002F reconstruct PRNG state and predict the next token.\n","javascript","",[14,74,75,84,99,141,147,154,160],{"__ignoreMap":72},[76,77,80],"span",{"class":78,"line":79},"line",1,[76,81,83],{"class":82},"sJ8bj","\u002F\u002F UNSAFE — predictable token\n",[76,85,87,91,95],{"class":78,"line":86},2,[76,88,90],{"class":89},"szBVR","function",[76,92,94],{"class":93},"sScJk"," generateToken",[76,96,98],{"class":97},"sVt8B","() {\n",[76,100,102,105,108,111,114,117,120,124,127,130,132,135,138],{"class":78,"line":101},3,[76,103,104],{"class":89},"  return",[76,106,107],{"class":97}," Math.",[76,109,110],{"class":93},"random",[76,112,113],{"class":97},"().",[76,115,116],{"class":93},"toString",[76,118,119],{"class":97},"(",[76,121,123],{"class":122},"sj4cs","36",[76,125,126],{"class":97},").",[76,128,129],{"class":93},"slice",[76,131,119],{"class":97},[76,133,134],{"class":122},"2",[76,136,137],{"class":97},")  ",[76,139,140],{"class":82},"\u002F\u002F Predictable!\n",[76,142,144],{"class":78,"line":143},4,[76,145,146],{"class":97},"}\n",[76,148,150],{"class":78,"line":149},5,[76,151,153],{"emptyLinePlaceholder":152},true,"\n",[76,155,157],{"class":78,"line":156},6,[76,158,159],{"class":82},"\u002F\u002F If attacker observes output \"k0a1b2c3d\", they can\n",[76,161,163],{"class":78,"line":162},7,[76,164,165],{"class":82},"\u002F\u002F reconstruct PRNG state and predict the next token.\n",[10,167,168,169,171],{},"The V8 team explicitly documents that ",[14,170,16],{}," is not cryptographically secure. It was never designed to be — it optimizes for speed, not unpredictability.",[22,173,175],{"id":174},"the-web-crypto-api","The Web Crypto API",[10,177,178,179,182],{},"The correct replacement is ",[14,180,181],{},"crypto.getRandomValues()",", part of the Web Crypto API. It fills a typed array with cryptographically secure random values drawn from the operating system's entropy pool.",[67,184,186],{"className":69,"code":185,"language":71,"meta":72,"style":72},"\u002F\u002F SAFER — cryptographically secure random bytes\nfunction generateSecureToken(length = 32) {\n  const bytes = new Uint8Array(length)\n  crypto.getRandomValues(bytes)\n  return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('')\n}\n\ngenerateSecureToken()  \u002F\u002F \"a3f1c8e72b4d9f0a6e5c3b8d1f7a2e4c...\"\n",[14,187,188,193,215,234,245,304,308,312],{"__ignoreMap":72},[76,189,190],{"class":78,"line":79},[76,191,192],{"class":82},"\u002F\u002F SAFER — cryptographically secure random bytes\n",[76,194,195,197,200,202,206,209,212],{"class":78,"line":86},[76,196,90],{"class":89},[76,198,199],{"class":93}," generateSecureToken",[76,201,119],{"class":97},[76,203,205],{"class":204},"s4XuR","length",[76,207,208],{"class":89}," =",[76,210,211],{"class":122}," 32",[76,213,214],{"class":97},") {\n",[76,216,217,220,223,225,228,231],{"class":78,"line":101},[76,218,219],{"class":89},"  const",[76,221,222],{"class":122}," bytes",[76,224,208],{"class":89},[76,226,227],{"class":89}," new",[76,229,230],{"class":93}," Uint8Array",[76,232,233],{"class":97},"(length)\n",[76,235,236,239,242],{"class":78,"line":143},[76,237,238],{"class":97},"  crypto.",[76,240,241],{"class":93},"getRandomValues",[76,243,244],{"class":97},"(bytes)\n",[76,246,247,249,252,255,258,261,264,267,269,271,274,276,279,281,283,286,290,293,296,298,301],{"class":78,"line":149},[76,248,104],{"class":89},[76,250,251],{"class":97}," Array.",[76,253,254],{"class":93},"from",[76,256,257],{"class":97},"(bytes, ",[76,259,260],{"class":204},"b",[76,262,263],{"class":89}," =>",[76,265,266],{"class":97}," b.",[76,268,116],{"class":93},[76,270,119],{"class":97},[76,272,273],{"class":122},"16",[76,275,126],{"class":97},[76,277,278],{"class":93},"padStart",[76,280,119],{"class":97},[76,282,134],{"class":122},[76,284,285],{"class":97},", ",[76,287,289],{"class":288},"sZZnC","'0'",[76,291,292],{"class":97},")).",[76,294,295],{"class":93},"join",[76,297,119],{"class":97},[76,299,300],{"class":288},"''",[76,302,303],{"class":97},")\n",[76,305,306],{"class":78,"line":156},[76,307,146],{"class":97},[76,309,310],{"class":78,"line":162},[76,311,153],{"emptyLinePlaceholder":152},[76,313,315,318,321],{"class":78,"line":314},8,[76,316,317],{"class":93},"generateSecureToken",[76,319,320],{"class":97},"()  ",[76,322,323],{"class":82},"\u002F\u002F \"a3f1c8e72b4d9f0a6e5c3b8d1f7a2e4c...\"\n",[10,325,326,327,329],{},"Key properties of ",[14,328,181],{},":",[331,332,333,347],"table",{},[334,335,336],"thead",{},[337,338,339,343,345],"tr",{},[340,341,342],"th",{},"Property",[340,344,16],{},[340,346,181],{},[348,349,350,362,373,384,395,406],"tbody",{},[337,351,352,356,359],{},[353,354,355],"td",{},"Cryptographically secure",[353,357,358],{},"No",[353,360,361],{},"Yes",[337,363,364,367,370],{},[353,365,366],{},"Speed",[353,368,369],{},"Very fast",[353,371,372],{},"Fast (system call overhead)",[337,374,375,378,381],{},[353,376,377],{},"Output range",[353,379,380],{},"[0, 1) float",[353,382,383],{},"0–255 per byte",[337,385,386,389,392],{},[353,387,388],{},"Deterministic",[353,390,391],{},"Yes (PRNG)",[353,393,394],{},"No (OS entropy)",[337,396,397,400,403],{},[353,398,399],{},"Cross-origin isolation",[353,401,402],{},"Shared state",[353,404,405],{},"Per-call from OS pool",[337,407,408,411,413],{},[353,409,410],{},"Available in workers",[353,412,361],{},[353,414,361],{},[10,416,417,418,421,422,126],{},"The function is available in all modern browsers and Node.js (as ",[14,419,420],{},"crypto.webcrypto.getRandomValues()"," or the older ",[14,423,424],{},"require('crypto').randomBytes()",[22,426,428],{"id":427},"common-token-generation-patterns","Common Token Generation Patterns",[10,430,431,434],{},[41,432,433],{},"Session tokens"," — 256 bits minimum:",[67,436,438],{"className":69,"code":437,"language":71,"meta":72,"style":72},"function generateSessionToken() {\n  const bytes = new Uint8Array(32)  \u002F\u002F 256 bits\n  crypto.getRandomValues(bytes)\n  return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('')\n}\n",[14,439,440,449,471,479,523],{"__ignoreMap":72},[76,441,442,444,447],{"class":78,"line":79},[76,443,90],{"class":89},[76,445,446],{"class":93}," generateSessionToken",[76,448,98],{"class":97},[76,450,451,453,455,457,459,461,463,466,468],{"class":78,"line":86},[76,452,219],{"class":89},[76,454,222],{"class":122},[76,456,208],{"class":89},[76,458,227],{"class":89},[76,460,230],{"class":93},[76,462,119],{"class":97},[76,464,465],{"class":122},"32",[76,467,137],{"class":97},[76,469,470],{"class":82},"\u002F\u002F 256 bits\n",[76,472,473,475,477],{"class":78,"line":101},[76,474,238],{"class":97},[76,476,241],{"class":93},[76,478,244],{"class":97},[76,480,481,483,485,487,489,491,493,495,497,499,501,503,505,507,509,511,513,515,517,519,521],{"class":78,"line":143},[76,482,104],{"class":89},[76,484,251],{"class":97},[76,486,254],{"class":93},[76,488,257],{"class":97},[76,490,260],{"class":204},[76,492,263],{"class":89},[76,494,266],{"class":97},[76,496,116],{"class":93},[76,498,119],{"class":97},[76,500,273],{"class":122},[76,502,126],{"class":97},[76,504,278],{"class":93},[76,506,119],{"class":97},[76,508,134],{"class":122},[76,510,285],{"class":97},[76,512,289],{"class":288},[76,514,292],{"class":97},[76,516,295],{"class":93},[76,518,119],{"class":97},[76,520,300],{"class":288},[76,522,303],{"class":97},[76,524,525],{"class":78,"line":149},[76,526,146],{"class":97},[10,528,529,532],{},[41,530,531],{},"URL-safe tokens"," — base64url encoding avoids special characters:",[67,534,536],{"className":69,"code":535,"language":71,"meta":72,"style":72},"function generateUrlSafeToken(length = 24) {\n  const bytes = new Uint8Array(length)\n  crypto.getRandomValues(bytes)\n  const binary = String.fromCharCode(...bytes)\n  return btoa(binary)\n    .replace(\u002F\\+\u002Fg, '-')\n    .replace(\u002F\\\u002F\u002Fg, '_')\n    .replace(\u002F=+$\u002F, '')\n}\n",[14,537,538,556,570,578,601,611,640,664,689],{"__ignoreMap":72},[76,539,540,542,545,547,549,551,554],{"class":78,"line":79},[76,541,90],{"class":89},[76,543,544],{"class":93}," generateUrlSafeToken",[76,546,119],{"class":97},[76,548,205],{"class":204},[76,550,208],{"class":89},[76,552,553],{"class":122}," 24",[76,555,214],{"class":97},[76,557,558,560,562,564,566,568],{"class":78,"line":86},[76,559,219],{"class":89},[76,561,222],{"class":122},[76,563,208],{"class":89},[76,565,227],{"class":89},[76,567,230],{"class":93},[76,569,233],{"class":97},[76,571,572,574,576],{"class":78,"line":101},[76,573,238],{"class":97},[76,575,241],{"class":93},[76,577,244],{"class":97},[76,579,580,582,585,587,590,593,595,598],{"class":78,"line":143},[76,581,219],{"class":89},[76,583,584],{"class":122}," binary",[76,586,208],{"class":89},[76,588,589],{"class":97}," String.",[76,591,592],{"class":93},"fromCharCode",[76,594,119],{"class":97},[76,596,597],{"class":89},"...",[76,599,600],{"class":97},"bytes)\n",[76,602,603,605,608],{"class":78,"line":149},[76,604,104],{"class":89},[76,606,607],{"class":93}," btoa",[76,609,610],{"class":97},"(binary)\n",[76,612,613,616,619,621,624,628,630,633,635,638],{"class":78,"line":156},[76,614,615],{"class":97},"    .",[76,617,618],{"class":93},"replace",[76,620,119],{"class":97},[76,622,623],{"class":288},"\u002F",[76,625,627],{"class":626},"snhLl","\\+",[76,629,623],{"class":288},[76,631,632],{"class":89},"g",[76,634,285],{"class":97},[76,636,637],{"class":288},"'-'",[76,639,303],{"class":97},[76,641,642,644,646,648,650,653,655,657,659,662],{"class":78,"line":162},[76,643,615],{"class":97},[76,645,618],{"class":93},[76,647,119],{"class":97},[76,649,623],{"class":288},[76,651,652],{"class":626},"\\\u002F",[76,654,623],{"class":288},[76,656,632],{"class":89},[76,658,285],{"class":97},[76,660,661],{"class":288},"'_'",[76,663,303],{"class":97},[76,665,666,668,670,672,674,678,681,683,685,687],{"class":78,"line":314},[76,667,615],{"class":97},[76,669,618],{"class":93},[76,671,119],{"class":97},[76,673,623],{"class":288},[76,675,677],{"class":676},"sA_wV","=",[76,679,680],{"class":89},"+$",[76,682,623],{"class":288},[76,684,285],{"class":97},[76,686,300],{"class":288},[76,688,303],{"class":97},[76,690,692],{"class":78,"line":691},9,[76,693,146],{"class":97},[10,695,696,699],{},[41,697,698],{},"OTP (one-time password) codes"," — numeric only, fixed length:",[67,701,703],{"className":69,"code":702,"language":71,"meta":72,"style":72},"function generateOTP(length = 6) {\n  const bytes = new Uint8Array(length)\n  crypto.getRandomValues(bytes)\n  return Array.from(bytes, b => b % 10).join('')\n}\n",[14,704,705,723,737,745,778],{"__ignoreMap":72},[76,706,707,709,712,714,716,718,721],{"class":78,"line":79},[76,708,90],{"class":89},[76,710,711],{"class":93}," generateOTP",[76,713,119],{"class":97},[76,715,205],{"class":204},[76,717,208],{"class":89},[76,719,720],{"class":122}," 6",[76,722,214],{"class":97},[76,724,725,727,729,731,733,735],{"class":78,"line":86},[76,726,219],{"class":89},[76,728,222],{"class":122},[76,730,208],{"class":89},[76,732,227],{"class":89},[76,734,230],{"class":93},[76,736,233],{"class":97},[76,738,739,741,743],{"class":78,"line":101},[76,740,238],{"class":97},[76,742,241],{"class":93},[76,744,244],{"class":97},[76,746,747,749,751,753,755,757,759,762,765,768,770,772,774,776],{"class":78,"line":143},[76,748,104],{"class":89},[76,750,251],{"class":97},[76,752,254],{"class":93},[76,754,257],{"class":97},[76,756,260],{"class":204},[76,758,263],{"class":89},[76,760,761],{"class":97}," b ",[76,763,764],{"class":89},"%",[76,766,767],{"class":122}," 10",[76,769,126],{"class":97},[76,771,295],{"class":93},[76,773,119],{"class":97},[76,775,300],{"class":288},[76,777,303],{"class":97},[76,779,780],{"class":78,"line":149},[76,781,146],{"class":97},[10,783,784,785,788],{},"The modulo bias in ",[14,786,787],{},"b % 10"," is negligible for 6-digit OTPs (bias is 6\u002F256 ≈ 2.3% per digit). For high-value tokens, use rejection sampling instead:",[67,790,792],{"className":69,"code":791,"language":71,"meta":72,"style":72},"function generateUnbiasedOTP(length = 6) {\n  const digits = []\n  while (digits.length \u003C length) {\n    const bytes = new Uint8Array(1)\n    crypto.getRandomValues(bytes)\n    if (bytes[0] \u003C 240) {  \u002F\u002F 240 = 10 * 24, avoids bias\n      digits.push((bytes[0] % 10).toString())\n    }\n  }\n  return digits.join('')\n}\n",[14,793,794,811,823,839,859,868,894,920,925,930,946],{"__ignoreMap":72},[76,795,796,798,801,803,805,807,809],{"class":78,"line":79},[76,797,90],{"class":89},[76,799,800],{"class":93}," generateUnbiasedOTP",[76,802,119],{"class":97},[76,804,205],{"class":204},[76,806,208],{"class":89},[76,808,720],{"class":122},[76,810,214],{"class":97},[76,812,813,815,818,820],{"class":78,"line":86},[76,814,219],{"class":89},[76,816,817],{"class":122}," digits",[76,819,208],{"class":89},[76,821,822],{"class":97}," []\n",[76,824,825,828,831,833,836],{"class":78,"line":101},[76,826,827],{"class":89},"  while",[76,829,830],{"class":97}," (digits.",[76,832,205],{"class":122},[76,834,835],{"class":89}," \u003C",[76,837,838],{"class":97}," length) {\n",[76,840,841,844,846,848,850,852,854,857],{"class":78,"line":143},[76,842,843],{"class":89},"    const",[76,845,222],{"class":122},[76,847,208],{"class":89},[76,849,227],{"class":89},[76,851,230],{"class":93},[76,853,119],{"class":97},[76,855,856],{"class":122},"1",[76,858,303],{"class":97},[76,860,861,864,866],{"class":78,"line":149},[76,862,863],{"class":97},"    crypto.",[76,865,241],{"class":93},[76,867,244],{"class":97},[76,869,870,873,876,879,882,885,888,891],{"class":78,"line":156},[76,871,872],{"class":89},"    if",[76,874,875],{"class":97}," (bytes[",[76,877,878],{"class":122},"0",[76,880,881],{"class":97},"] ",[76,883,884],{"class":89},"\u003C",[76,886,887],{"class":122}," 240",[76,889,890],{"class":97},") {  ",[76,892,893],{"class":82},"\u002F\u002F 240 = 10 * 24, avoids bias\n",[76,895,896,899,902,905,907,909,911,913,915,917],{"class":78,"line":162},[76,897,898],{"class":97},"      digits.",[76,900,901],{"class":93},"push",[76,903,904],{"class":97},"((bytes[",[76,906,878],{"class":122},[76,908,881],{"class":97},[76,910,764],{"class":89},[76,912,767],{"class":122},[76,914,126],{"class":97},[76,916,116],{"class":93},[76,918,919],{"class":97},"())\n",[76,921,922],{"class":78,"line":314},[76,923,924],{"class":97},"    }\n",[76,926,927],{"class":78,"line":691},[76,928,929],{"class":97},"  }\n",[76,931,933,935,938,940,942,944],{"class":78,"line":932},10,[76,934,104],{"class":89},[76,936,937],{"class":97}," digits.",[76,939,295],{"class":93},[76,941,119],{"class":97},[76,943,300],{"class":288},[76,945,303],{"class":97},[76,947,949],{"class":78,"line":948},11,[76,950,146],{"class":97},[22,952,954],{"id":953},"nodejs-server-side-rng","Node.js Server-Side RNG",[10,956,957,958,961],{},"On the server, use the ",[14,959,960],{},"node:crypto"," module:",[67,963,965],{"className":69,"code":964,"language":71,"meta":72,"style":72},"import { randomBytes, randomInt, randomUUID } from 'node:crypto'\n\n\u002F\u002F Random bytes for tokens\nconst token = randomBytes(32).toString('hex')\n\n\u002F\u002F Random integer in range [0, max) without modulo bias\nconst index = randomInt(0, array.length)\n\n\u002F\u002F UUID v4 — cryptographically random\nconst id = randomUUID()\n",[14,966,967,980,984,989,1017,1021,1026,1049,1053,1058],{"__ignoreMap":72},[76,968,969,972,975,977],{"class":78,"line":79},[76,970,971],{"class":89},"import",[76,973,974],{"class":97}," { randomBytes, randomInt, randomUUID } ",[76,976,254],{"class":89},[76,978,979],{"class":288}," 'node:crypto'\n",[76,981,982],{"class":78,"line":86},[76,983,153],{"emptyLinePlaceholder":152},[76,985,986],{"class":78,"line":101},[76,987,988],{"class":82},"\u002F\u002F Random bytes for tokens\n",[76,990,991,994,997,999,1002,1004,1006,1008,1010,1012,1015],{"class":78,"line":143},[76,992,993],{"class":89},"const",[76,995,996],{"class":122}," token",[76,998,208],{"class":89},[76,1000,1001],{"class":93}," randomBytes",[76,1003,119],{"class":97},[76,1005,465],{"class":122},[76,1007,126],{"class":97},[76,1009,116],{"class":93},[76,1011,119],{"class":97},[76,1013,1014],{"class":288},"'hex'",[76,1016,303],{"class":97},[76,1018,1019],{"class":78,"line":149},[76,1020,153],{"emptyLinePlaceholder":152},[76,1022,1023],{"class":78,"line":156},[76,1024,1025],{"class":82},"\u002F\u002F Random integer in range [0, max) without modulo bias\n",[76,1027,1028,1030,1033,1035,1038,1040,1042,1045,1047],{"class":78,"line":162},[76,1029,993],{"class":89},[76,1031,1032],{"class":122}," index",[76,1034,208],{"class":89},[76,1036,1037],{"class":93}," randomInt",[76,1039,119],{"class":97},[76,1041,878],{"class":122},[76,1043,1044],{"class":97},", array.",[76,1046,205],{"class":122},[76,1048,303],{"class":97},[76,1050,1051],{"class":78,"line":314},[76,1052,153],{"emptyLinePlaceholder":152},[76,1054,1055],{"class":78,"line":691},[76,1056,1057],{"class":82},"\u002F\u002F UUID v4 — cryptographically random\n",[76,1059,1060,1062,1065,1067,1070],{"class":78,"line":932},[76,1061,993],{"class":89},[76,1063,1064],{"class":122}," id",[76,1066,208],{"class":89},[76,1068,1069],{"class":93}," randomUUID",[76,1071,1072],{"class":97},"()\n",[10,1074,1075,1078,1079,1082,1083,1086],{},[14,1076,1077],{},"randomBytes"," reads from ",[14,1080,1081],{},"\u002Fdev\u002Furandom"," on Linux and ",[14,1084,1085],{},"BCryptGenRandom"," on Windows. Both are CSPRNGs backed by OS-collected entropy.",[22,1088,1090],{"id":1089},"when-to-use-each","When to Use Each",[331,1092,1093,1103],{},[334,1094,1095],{},[337,1096,1097,1100],{},[340,1098,1099],{},"Use Case",[340,1101,1102],{},"Correct API",[348,1104,1105,1117,1128,1139,1152,1162,1171],{},[337,1106,1107,1109],{},[353,1108,433],{},[353,1110,1111,1113,1114],{},[14,1112,181],{}," \u002F ",[14,1115,1116],{},"randomBytes()",[337,1118,1119,1122],{},[353,1120,1121],{},"Password reset links",[353,1123,1124,1113,1126],{},[14,1125,181],{},[14,1127,1116],{},[337,1129,1130,1133],{},[353,1131,1132],{},"CSRF tokens",[353,1134,1135,1113,1137],{},[14,1136,181],{},[14,1138,1116],{},[337,1140,1141,1144],{},[353,1142,1143],{},"UUID v4",[353,1145,1146,1149,1150],{},[14,1147,1148],{},"randomUUID()"," or ",[14,1151,181],{},[337,1153,1154,1157],{},[353,1155,1156],{},"Shuffling a deck in a game",[353,1158,1159,1161],{},[14,1160,16],{}," (fine for non-security)",[337,1163,1164,1167],{},[353,1165,1166],{},"A\u002FB test assignment",[353,1168,1169,1161],{},[14,1170,16],{},[337,1172,1173,1176],{},[353,1174,1175],{},"CSS animation offset",[353,1177,1178,1161],{},[14,1179,16],{},[10,1181,1182,1183,1185,1186,1188],{},"The rule is simple: if the random value protects access, identity, or money, use ",[14,1184,181],{},". Everything else can use ",[14,1187,16],{}," without risk.",[10,1190,1191,1192,1196],{},"Generate secure random numbers for development and testing at ",[1193,1194,1195],"a",{"href":1195},"\u002Ftools\u002Frandom-number",", which supports both standard and cryptographic random generation modes.",[1198,1199,1200],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .snhLl, html code.shiki .snhLl{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}",{"title":72,"searchDepth":86,"depth":86,"links":1202},[1203,1204,1205,1206,1207],{"id":24,"depth":86,"text":25},{"id":174,"depth":86,"text":175},{"id":427,"depth":86,"text":428},{"id":953,"depth":86,"text":954},{"id":1089,"depth":86,"text":1090},"2026-05-28","Why Math.random() is unsafe for tokens and how crypto.getRandomValues() fixes it.","md",{},"\u002Fguides\u002Fcsprng-security-guide",{"title":5,"description":1209},"guides\u002Fcsprng-security-guide","-ruytfMZWeXKxeDtRnIBcKQJJWsUA-8f_EVyIS1PogU",1780401332604]