<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tim O.</title>
    <description>The latest articles on DEV Community by Tim O. (@tim_o_5617baa5171354e).</description>
    <link>https://dev.to/tim_o_5617baa5171354e</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3872111%2F73072cf5-24b7-4a75-8a43-5e8ba773d723.png</url>
      <title>DEV Community: Tim O.</title>
      <link>https://dev.to/tim_o_5617baa5171354e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tim_o_5617baa5171354e"/>
    <language>en</language>
    <item>
      <title>Why Math.random() Will Fail Your Next Security Audit</title>
      <dc:creator>Tim O.</dc:creator>
      <pubDate>Fri, 10 Apr 2026 16:38:41 +0000</pubDate>
      <link>https://dev.to/tim_o_5617baa5171354e/why-mathrandom-will-fail-your-next-security-audit-4h2c</link>
      <guid>https://dev.to/tim_o_5617baa5171354e/why-mathrandom-will-fail-your-next-security-audit-4h2c</guid>
      <description>&lt;p&gt;If you have ever written something like this in a production codebase:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const secret = Math.random().toString(36).slice(2);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You have shipped a credential that will fail a security audit.&lt;br&gt;
Not might fail. Will fail.&lt;br&gt;
Here is why that matters and what to do about it before your auditors find it first.&lt;br&gt;
With the renewed focus on Executive Order 14028 and software supply chain security, auditors are no longer just looking at what libraries you use. They are looking at how you use them to generate internal secrets. Credential generation is now explicitly in scope for supply chain security reviews in ways it was not three years ago.&lt;br&gt;
The problem with Math.random()&lt;br&gt;
Math.random() is not a cryptographic function. It was never designed to be. It generates pseudorandom numbers that are fast and statistically distributed enough for things like shuffling a playlist or generating a random color. It is completely wrong for generating secrets, API keys, passwords, or any credential that needs to be unpredictable to an adversary.&lt;br&gt;
The specific problem is predictability. Math.random() uses a deterministic algorithm seeded by the JavaScript engine. In some environments that seed is observable or reconstructible. In all environments the output fails the entropy requirements defined in NIST 800-63B, the federal standard for digital identity and credential strength.&lt;br&gt;
When an auditor runs entropy analysis on your generated credentials and finds Math.random() in the generation path, the finding looks like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AUDIT FINDING — CRITICAL&lt;br&gt;
Control: SC-28 / NIST 800-53&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finding: Credential generation function Math.random()&lt;br&gt;
identified across microservices.&lt;/p&gt;

&lt;p&gt;Impact: Generated secrets fail entropy requirements&lt;br&gt;
for NIST 800-63B compliance. Cryptographic randomness&lt;br&gt;
cannot be verified.&lt;/p&gt;

&lt;p&gt;Status: OPEN - 90 day remediation required&lt;/p&gt;

&lt;p&gt;That finding stops deals, delays launches, and costs engineering time to remediate retroactively. The fix at that point is expensive because you have to find every place Math.random() was used, replace it, rotate the affected credentials, and produce documentation proving the new generation method is compliant.&lt;br&gt;
The right function is already in Node.js&lt;br&gt;
You do not need a library. You do not need a third party service. Node.js ships with a cryptographically secure random number generator in the built-in crypto module:&lt;/p&gt;

&lt;p&gt;`const { randomInt, randomBytes } = require('crypto');&lt;/p&gt;

&lt;p&gt;// Generate a random integer between 0 and charset.length&lt;br&gt;
const index = randomInt(0, charset.length);&lt;/p&gt;

&lt;p&gt;// Generate random bytes for a hex token&lt;br&gt;
const token = randomBytes(32).toString('hex');`&lt;/p&gt;

&lt;p&gt;crypto.randomInt() uses the operating system's cryptographically secure pseudorandom number generator. On Linux that is /dev/urandom. The output passes NIST entropy requirements because the source of randomness is designed for exactly this purpose.&lt;br&gt;
The fix is a one line change in most cases. The problem is that most teams never make it because nobody flags it until an auditor does.&lt;br&gt;
A copy-paste utility for your codebase&lt;/p&gt;

&lt;p&gt;If you want a drop-in replacement you can put in your utils/ folder today, here is a minimal version using only Node.js built-ins:&lt;/p&gt;

&lt;p&gt;`const { randomInt } = require('crypto');&lt;/p&gt;

&lt;p&gt;const CHARSETS = {&lt;br&gt;
  uppercase: 'ABCDEFGHJKLMNPQRSTUVWXYZ',&lt;br&gt;
  lowercase: 'abcdefghjkmnpqrstuvwxyz',&lt;br&gt;
  numbers: '23456789',&lt;br&gt;
  symbols: '!@#$%^&amp;amp;*'&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;function generateSecureCredential(length = 20, options = {}) {&lt;br&gt;
  const {&lt;br&gt;
    uppercase = true,&lt;br&gt;
    lowercase = true,&lt;br&gt;
    numbers = true,&lt;br&gt;
    symbols = false&lt;br&gt;
  } = options;&lt;/p&gt;

&lt;p&gt;let charset = '';&lt;br&gt;
  if (uppercase) charset += CHARSETS.uppercase;&lt;br&gt;
  if (lowercase) charset += CHARSETS.lowercase;&lt;br&gt;
  if (numbers) charset += CHARSETS.numbers;&lt;br&gt;
  if (symbols) charset += CHARSETS.symbols;&lt;/p&gt;

&lt;p&gt;if (!charset) throw new Error('At least one character set required');&lt;/p&gt;

&lt;p&gt;return Array.from(&lt;br&gt;
    { length },&lt;br&gt;
    () =&amp;gt; charset[randomInt(0, charset.length)]&lt;br&gt;
  ).join('');&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// Usage&lt;br&gt;
const secret = generateSecureCredential(20, {&lt;br&gt;
  uppercase: true,&lt;br&gt;
  lowercase: true,&lt;br&gt;
  numbers: true,&lt;br&gt;
  symbols: true&lt;br&gt;
});`&lt;/p&gt;

&lt;p&gt;This gives you cryptographic security but not compliance documentation. For NIST 800-63B audit trails, entropy calculation, and machine-readable compliance metadata, that is what you need to add on top of this foundation.&lt;br&gt;
The documentation gap nobody talks about&lt;br&gt;
Switching from Math.random() to crypto.randomInt() is the technical fix. But it does not solve the audit problem completely.&lt;br&gt;
Auditors do not just want secure credentials. They want documented proof that credentials are secure. That means:&lt;/p&gt;

&lt;p&gt;Entropy bits calculated per credential&lt;br&gt;
Generation method documented at the time of creation&lt;br&gt;
Compliance profile recorded alongside the credential&lt;/p&gt;

&lt;p&gt;Most internal credential generation systems do not produce this documentation automatically. Engineers have to build it separately, maintain it, and make sure it stays accurate as the codebase evolves. Most teams never complete that work.&lt;br&gt;
This is the gap that causes audit findings even when the underlying generation is technically correct. You fixed Math.random() but you have no proof you fixed it that satisfies an auditor asking for evidence.&lt;br&gt;
What shift-left credential security looks like&lt;br&gt;
The concept of shifting security left means solving security problems at the earliest possible point in the development workflow, not after auditors find them.&lt;br&gt;
For credential generation that means every credential that ships should come with documented proof of how it was generated, what entropy it has, and which compliance standard it meets. That documentation should be automatic, not something an engineer produces manually six months later when someone asks for it.&lt;br&gt;
If you want to automate that documentation piece entirely, this is how we structured the Six Sense API to handle it:&lt;/p&gt;

&lt;p&gt;`const response = await fetch('&lt;a href="https://api.sixsensesolutions.net/v1/generate" rel="noopener noreferrer"&gt;https://api.sixsensesolutions.net/v1/generate&lt;/a&gt;', {&lt;br&gt;
  method: 'POST',&lt;br&gt;
  headers: {&lt;br&gt;
    'Authorization': 'Bearer your_api_key',&lt;br&gt;
    'Content-Type': 'application/json'&lt;br&gt;
  },&lt;br&gt;
  body: JSON.stringify({&lt;br&gt;
    length: 20,&lt;br&gt;
    quantity: 1,&lt;br&gt;
    compliance: 'NIST',&lt;br&gt;
    options: {&lt;br&gt;
      uppercase: true,&lt;br&gt;
      lowercase: true,&lt;br&gt;
      numbers: true,&lt;br&gt;
      symbols: true,&lt;br&gt;
      exclude_ambiguous: true&lt;br&gt;
    }&lt;br&gt;
  })&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;const { passwords, meta } = await response.json();&lt;/p&gt;

&lt;p&gt;console.log(meta);&lt;br&gt;
// {&lt;br&gt;
//   length: 20,&lt;br&gt;
//   entropy_bits: 120.4,  // &amp;lt;-- This is your audit evidence. 120+ bits exceeds NIST minimum.&lt;br&gt;
//   generated_at: "2026-04-10T14:57:35Z",&lt;br&gt;
//   compliance_profile: "NIST",&lt;br&gt;
//   calls_remaining: 49999&lt;br&gt;
// }`&lt;/p&gt;

&lt;p&gt;Every response includes entropy_bits and compliance_profile in the metadata. That metadata is the audit documentation. Your auditor asks for proof that credentials meet NIST 800-63B. You show them the response metadata. The finding closes.&lt;br&gt;
The free tier&lt;br&gt;
If you want to test this in your own codebase, there is a free tier at sixsensesolutions.net with 500 calls per month and no credit card required. Signup generates a real API key instantly.&lt;br&gt;
The NIST and SOC2 compliance profiles enforce minimum lengths, character requirements, and ambiguous character exclusion automatically. The strong profile respects whatever options you pass directly.&lt;br&gt;
The takeaway&lt;br&gt;
If your codebase contains Math.random() in any credential generation path, you have a finding waiting to happen. The technical fix is one line and the copy-paste utility above gives you that today for free. The documentation fix is what most teams skip and what auditors actually ask for.&lt;br&gt;
Both need to be in place before your next audit, not after.&lt;/p&gt;

</description>
      <category>security</category>
      <category>javascript</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
