<?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>Forem: T.O.</title>
    <description>The latest articles on Forem by T.O. (@tim_o_5617baa5171354e).</description>
    <link>https://forem.com/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>Forem: T.O.</title>
      <link>https://forem.com/tim_o_5617baa5171354e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tim_o_5617baa5171354e"/>
    <language>en</language>
    <item>
      <title>Stop Treating Credential Generation as an Auditor Scramble</title>
      <dc:creator>T.O.</dc:creator>
      <pubDate>Tue, 14 Apr 2026 13:52:20 +0000</pubDate>
      <link>https://forem.com/tim_o_5617baa5171354e/stop-treating-credential-generation-as-an-auditor-scramble-3742</link>
      <guid>https://forem.com/tim_o_5617baa5171354e/stop-treating-credential-generation-as-an-auditor-scramble-3742</guid>
      <description>&lt;p&gt;&lt;em&gt;How to bake compliance evidence into the process before your next SOC2 or HIPAA audit.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The pattern shows up the same way every time. Audit prep starts and someone asks for proof that credentials were generated securely. Engineers scramble to pull Git history, check deployment logs, and screenshot password manager settings. They end up writing a three paragraph explanation of what they think their generation process does.&lt;/p&gt;

&lt;p&gt;The auditor reviews it and says the evidence is insufficient.&lt;/p&gt;

&lt;p&gt;Manual reconstruction always turns into pain. Not sometimes. Every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why manual reconstruction fails auditors
&lt;/h2&gt;

&lt;p&gt;Auditors are not asking whether you have a good process today. They are asking whether you had a documented, consistent, repeatable process at the time each credential was generated. Those are two completely different questions.&lt;/p&gt;

&lt;p&gt;You can have an excellent process right now and still fail an audit because you cannot prove what was true six months ago. The entropy level, the compliance policy, the timestamp, the system that generated it. If that information was not captured at creation time it effectively does not exist. Retroactive reconstruction is not evidence. It is a story.&lt;/p&gt;

&lt;h2&gt;
  
  
  What baking evidence into the process actually means
&lt;/h2&gt;

&lt;p&gt;The scalable answer is not better documentation or more screenshots. It is making evidence generation automatic and inseparable from the credential generation itself.&lt;/p&gt;

&lt;p&gt;Every time a credential is created these five things should be captured automatically in the same operation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The generation method.&lt;/strong&gt; Not just the library name. The specific cryptographic function, the entropy source, and the parameters applied.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The compliance policy.&lt;/strong&gt; Which standard was active at the moment of creation. NIST 800-63B, SOC2, HIPAA, or your internal standard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The entropy bits.&lt;/strong&gt; The mathematical proof of randomness. This is the specific number auditors increasingly demand and most teams cannot produce.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The timestamp.&lt;/strong&gt; Exact ISO format with timezone. Not an approximation reconstructed from logs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The environment and caller.&lt;/strong&gt; Which system requested the credential and in which environment.&lt;/p&gt;

&lt;p&gt;When these are captured automatically in every generation event you move from telling stories to providing compliance artifacts. The auditor asks for proof. You pull the log. Finding closed.&lt;/p&gt;

&lt;p&gt;Here is what that structured log event looks like in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"credential.generated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"generated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-13T14:32:01.847Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compliance_profile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SOC2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"entropy_bits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;116&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"length"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"crypto.randomInt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"caller"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user-provisioning-service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"calls_remaining"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;49847&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every field exists at creation time. Nothing reconstructed. Nothing approximated.&lt;/p&gt;

&lt;h2&gt;
  
  
  The written standard most teams skip
&lt;/h2&gt;

&lt;p&gt;Before you can automate evidence you need a written standard. Most teams skip this because it feels like paperwork. It is not paperwork. It is the specification your automation implements.&lt;/p&gt;

&lt;p&gt;Your standard should answer these questions in writing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimum credential length per environment&lt;/li&gt;
&lt;li&gt;Entropy threshold that constitutes compliance with your applicable standard&lt;/li&gt;
&lt;li&gt;Which compliance profile applies to which system type&lt;/li&gt;
&lt;li&gt;Log retention period and access controls&lt;/li&gt;
&lt;li&gt;Who can query the audit log and under what conditions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without written standards your automation is generating evidence for a policy that does not exist on paper. Auditors will note that gap separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shift that changes everything
&lt;/h2&gt;

&lt;p&gt;The teams that handle audits well are not necessarily the ones with the best security. They are the ones whose security controls speak auditor language automatically.&lt;/p&gt;

&lt;p&gt;A credential generated with &lt;code&gt;crypto.randomInt&lt;/code&gt; is objectively more secure than one generated with &lt;code&gt;Math.random&lt;/code&gt;. But if neither produces a compliance artifact at creation time they look identical to an auditor. Zero evidence is zero evidence regardless of which function generated the credential.&lt;/p&gt;

&lt;p&gt;The goal is not just generating secure credentials. The goal is generating secure credentials that prove their own integrity the moment they are created. That proof has to exist at creation time. Not when your auditor asks for it.&lt;/p&gt;




</description>
      <category>security</category>
      <category>devops</category>
      <category>privacy</category>
      <category>devsecops</category>
    </item>
    <item>
      <title>Why Math.random() Will Fail Your Next Security Audit</title>
      <dc:creator>T.O.</dc:creator>
      <pubDate>Fri, 10 Apr 2026 16:38:41 +0000</pubDate>
      <link>https://forem.com/tim_o_5617baa5171354e/why-mathrandom-will-fail-your-next-security-audit-4h2c</link>
      <guid>https://forem.com/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>
