<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged securityarchitecture#networksecurity</title>
<link>https://coderlegion.com/tag/securityarchitecture%23networksecurity</link>
<description>Powered by Question2Answer</description>
<item>
<title>What Is Undefined Behavior (UB) and Why Is It Dangerous?</title>
<link>https://coderlegion.com/29876/what-is-undefined-behavior-ub-and-why-is-it-dangerous</link>
<description>&lt;p&gt;What Is Undefined Behavior (UB) and Why Is It Dangerous?&lt;/p&gt;
&lt;p&gt;In languages like Python or Java, a program either works or throws an error. In C and C++, things are different. Here we have UB — undefined behavior. This is a situation where the language standard does not say what the program should do. And then the compiler can do anything.&lt;/p&gt;
&lt;p&gt;What is UB&lt;/p&gt;
&lt;p&gt;Undefined behavior is code that is formally allowed for the compiler, but the result is unpredictable. The C standard does not describe what will happen — and the compiler is not required to warn you.&lt;/p&gt;
&lt;p&gt;Examples of UB:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Out-of-bounds array access

Division by zero

Dereferencing a NULL pointer

Using an uninitialized variable

Signed integer overflow

Modifying a variable twice in one expression (i = i++)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why it's dangerous&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Unpredictability — the program may work today and break tomorrow

Optimizations — the compiler uses UB to speed up code and breaks logic

Security — UB is a common cause of vulnerabilities

Debugging — UB is hard to find, because the bug doesn't always show up
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example&lt;br&gt;
c&lt;/p&gt;
&lt;p&gt;int arr[3];&lt;br&gt;
arr[5] = 10;  // out-of-bounds — UB&lt;/p&gt;
&lt;p&gt;This may:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;work and break nothing

corrupt a neighboring variable

cause a segmentation fault

do literally anything
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why the compiler doesn't save you&lt;/p&gt;
&lt;p&gt;The compiler optimizes code assuming that UB does not exist. If UB is present, the optimization can break the logic:&lt;br&gt;
c&lt;/p&gt;
&lt;p&gt;int x = 0;&lt;br&gt;
if (x + 1 &amp;gt; x) { ... }  // the compiler may remove this check&lt;/p&gt;
&lt;p&gt;Because signed integer overflow is UB. So the compiler is allowed to assume it won't happen.&lt;/p&gt;
&lt;p&gt;How to avoid UB&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Read the standard — know what is allowed

Enable warnings — -Wall -Wextra in GCC

Use sanitizers — -fsanitize=undefined,address

Write carefully — check bounds, pointers, initialization

Use Rust — UB is almost impossible there
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Conclusion&lt;/p&gt;
&lt;p&gt;UB is not a bug. It's a hole in the standard.&lt;br&gt;
The compiler is not required to catch it.&lt;br&gt;
A program may work for years — and break at the worst possible moment.&lt;/p&gt;
&lt;p&gt;Understanding UB is what separates a beginner from a systems programmer.&lt;br&gt;
If you know where your code can become unpredictable, you write reliable code.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29876/what-is-undefined-behavior-ub-and-why-is-it-dangerous</guid>
<pubDate>Mon, 05 Oct 2026 14:29:24 +0000</pubDate>
</item>
<item>
<title>Rate Limiting and API Security in Next.js 15 — The Patterns I Actually Use</title>
<link>https://coderlegion.com/25420/rate-limiting-and-api-security-in-next-js-15-the-patterns-i-actually-use</link>
<description>&lt;p&gt;An API route with no rate limiting works fine right up until someone hits it a thousand times a second, either by accident (a buggy client retry loop) or on purpose. I learned this the annoying way, watching a client's database bill spike because a public form endpoint had no limit on how often it could be called.&lt;/p&gt;
&lt;p&gt;Here is what I actually put in place now, on every project with public-facing endpoints.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-rate-limiting-with-upstash&quot;&gt;1. Rate Limiting with Upstash&lt;/h3&gt;
&lt;p&gt;Upstash's Redis-backed rate limiter works well with Next.js since it is designed for serverless, no persistent connection required between requests.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install @upstash/ratelimit @upstash/redis
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// lib/ratelimit.ts
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';

const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL as string,
  token: process.env.UPSTASH_REDIS_REST_TOKEN as string,
});

export const ratelimit = new Ratelimit({
  redis,
  limiter: Ratelimit.slidingWindow(10, '60 s'),
  analytics: true,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;slidingWindow(10, '60 s')&lt;/code&gt; allows 10 requests per 60 seconds per identifier. Sliding window is generally the better default over a fixed window, since a fixed window lets someone burst right at the boundary between two windows and effectively double their allowed rate.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-applying-it-to-a-route-handler&quot;&gt;2. Applying It to a Route Handler&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// app/api/contact/route.ts
import { ratelimit } from '@/lib/ratelimit';
import { headers } from 'next/headers';

export async function POST(request: Request) {
  const headersList = await headers();
  const ip = headersList.get('x-forwarded-for') ?? 'unknown';

  const { success, limit, remaining, reset } = await ratelimit.limit(ip);

  if (!success) {
    return Response.json(
      { error: 'Too many requests' },
      {
        status: 429,
        headers: {
          'X-RateLimit-Limit': limit.toString(),
          'X-RateLimit-Remaining': remaining.toString(),
          'X-RateLimit-Reset': reset.toString(),
        },
      }
    );
  }

  const body = await request.json();
  // handle the actual request
  return Response.json({ success: true });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Returning the rate limit headers even on success is good practice, it lets well-behaved clients see how close they are to the limit and back off proactively instead of finding out by hitting a 429.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-rate-limiting-server-actions&quot;&gt;3. Rate Limiting Server Actions&lt;/h3&gt;
&lt;p&gt;Server Actions do not get the same automatic IP access that route handlers do, since they are not standard HTTP endpoints in the same way. Getting the identifier takes an extra step.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// actions/contact.ts
'use server';
import { ratelimit } from '@/lib/ratelimit';
import { headers } from 'next/headers';

export async function submitContact(formData: FormData) {
  const headersList = await headers();
  const ip = headersList.get('x-forwarded-for') ?? 'unknown';

  const { success } = await ratelimit.limit(`contact_${ip}`);

  if (!success) {
    return { success: false, message: 'Too many attempts, try again shortly' };
  }

  // proceed with validation and saving
  return { success: true, message: 'Message sent' };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prefixing the identifier with &lt;code&gt;contact_&lt;/code&gt; matters if multiple Server Actions share one rate limiter, it keeps each action's usage counted separately instead of one action's traffic eating into another's limit.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-rate-limiting-by-user-not-just-ip&quot;&gt;4. Rate Limiting by User, Not Just IP&lt;/h3&gt;
&lt;p&gt;IP-based limiting is the right default for unauthenticated endpoints, like a public contact form. For authenticated actions, limiting by user ID is usually more accurate, since multiple users can share an IP behind the same network, and one user can rotate IPs to dodge an IP-based limit entirely.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// actions/posts.ts
'use server';
import { ratelimit } from '@/lib/ratelimit';
import { getSession } from '@/lib/auth';

export async function createPost(formData: FormData) {
  const session = await getSession();
  if (!session) throw new Error('Not authenticated');

  const { success } = await ratelimit.limit(`create_post_${session.userId}`);

  if (!success) {
    return { success: false, message: 'Slow down, try again in a minute' };
  }

  // proceed
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-validating-input-before-it-reaches-the-database&quot;&gt;5. Validating Input Before It Reaches the Database&lt;/h3&gt;
&lt;p&gt;Rate limiting controls how often someone can call an endpoint. It says nothing about what they send. Every route handler and Server Action still needs its own validation, same principle covered in the form handling setup, applied specifically at the API boundary here.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// app/api/users/route.ts
import { z } from 'zod';

const CreateUserSchema = z.object({
  name: z.string().min(2).max(50),
  email: z.string().email(),
});

export async function POST(request: Request) {
  const body = await request.json();
  const parsed = CreateUserSchema.safeParse(body);

  if (!parsed.success) {
    return Response.json({ error: 'Invalid input' }, { status: 400 });
  }

  // proceed with parsed.data, never the raw body
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Never pass the raw request body into a database call. Even with rate limiting in place, an endpoint accepting arbitrary shaped input is still open to malformed data, oversized payloads, or fields that were never meant to be settable from outside.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;6-cors-for-public-api-routes&quot;&gt;6. CORS for Public API Routes&lt;/h3&gt;
&lt;p&gt;If an API route is meant to be called from your own frontend only, restricting CORS prevents other websites from calling it directly from a browser using a visitor's already-authenticated session.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// app/api/data/route.ts
export async function GET(request: Request) {
  const origin = request.headers.get('origin');
  const allowedOrigin = process.env.NEXT_PUBLIC_URL;

  const response = Response.json({ data: 'example' });

  if (origin === allowedOrigin) {
    response.headers.set('Access-Control-Allow-Origin', allowedOrigin);
  }

  return response;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This matters specifically for cookie-based auth. Without a CORS restriction, a malicious site could make a request that rides on a logged-in user's cookies, and without a check like this, your API would happily respond as if the request came from your own frontend.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;7-secrets-never-reach-the-client&quot;&gt;7. Secrets Never Reach the Client&lt;/h3&gt;
&lt;p&gt;An easy mistake in Next.js specifically, any environment variable prefixed with &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; gets bundled into client-side JavaScript, visible to anyone who opens dev tools.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// ❌ This leaks the secret into the browser bundle
const apiKey = process.env.NEXT_PUBLIC_STRIPE_SECRET_KEY;

// ✅ Secret keys stay server-only, no NEXT_PUBLIC_ prefix
const apiKey = process.env.STRIPE_SECRET_KEY;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only prefix an environment variable with &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; when the value is genuinely meant to be public, a publishable Stripe key, an analytics ID, not an API secret, database URL, or anything meant to stay server-side only.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Pattern &lt;/th&gt;&lt;th&gt;Protects against&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Sliding window rate limit (Upstash) &lt;/td&gt;&lt;td&gt;Endpoint abuse, accidental retry loops, brute force attempts&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Per-user rate limiting on authenticated actions &lt;/td&gt;&lt;td&gt;IP rotation and shared-IP inaccuracy&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Zod validation at the API boundary &lt;/td&gt;&lt;td&gt;Malformed or unexpected input reaching the database&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CORS restriction on cookie-authenticated routes &lt;/td&gt;&lt;td&gt;Cross-site requests riding on a logged-in session&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;No &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; on secrets &lt;/td&gt;&lt;td&gt;API keys and secrets leaking into the client bundle&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Rate limiting and validation solve different problems and both matter. Rate limiting controls how often, validation controls what. An endpoint with one but not the other is still exposed, just in a different way.&lt;/p&gt;
&lt;p&gt;I put this exact set of protections, rate limiting through Upstash, Zod validation at every boundary, on any public-facing endpoint across the SaaS projects I build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Get the templates:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://pixelanas.gumroad.com&quot;&gt;https://pixelanas.gumroad.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Have you had an endpoint get hit hard enough to actually need rate limiting in production? Drop it below 👇&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Anas, full-stack Next.js developer building SaaS products and premium templates. X: &lt;a rel=&quot;nofollow&quot; href=&quot;https://x.com/ASheikh69751&quot;&gt;@ASheikh69751&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25420/rate-limiting-and-api-security-in-next-js-15-the-patterns-i-actually-use</guid>
<pubDate>Wed, 26 Aug 2026 08:33:06 +0000</pubDate>
</item>
<item>
<title>How to evaluate a small language model on multiple choice without fooling yourself</title>
<link>https://coderlegion.com/20374/how-to-evaluate-a-small-language-model-on-multiple-choice-without-fooling-yourself</link>
<description>&lt;p&gt;I spent months building GhostLM, a cybersecurity language model I trained from scratch in PyTorch. The architecture was the easy part. The thing that actually cost me weeks was evaluation. Three separate times, my benchmark told me something that was not true, and each time the bug was in the eval, not the model.&lt;/p&gt;
&lt;p&gt;This post is the protocol I wish I had started with. It is not specific to cybersecurity or to GhostLM. If you are evaluating any small model on a multiple-choice benchmark, these are the ways the number lies to you, and how to stop it.&lt;/p&gt;
&lt;h3 id=&quot;1-never-score-the-letter-score-the-option-text-&quot;&gt;1. Never score the letter. Score the option text.&lt;/h3&gt;
&lt;p&gt;The obvious way to grade a multiple-choice question is to ask the model for the probability of each letter and take the highest. So for a question with options A through D, you compare log P(&quot;A&quot;), log P(&quot;B&quot;), and so on.&lt;/p&gt;
&lt;p&gt;This is the single most dangerous shortcut in small-model evaluation, because it does not measure reasoning. It measures which letter the model likes.&lt;/p&gt;
&lt;p&gt;I had a model scoring 36.9% on a 2,500-question benchmark, comfortably above the 25% random baseline. Then I looked at the gold label distribution:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gold=A: 374 (15.0%)
gold=B: 813 (32.5%)
gold=C: 928 (37.1%)
gold=D: 385 (15.4%)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A model that emits &quot;C&quot; on every single question scores 37.1%. I checked the per-letter accuracy on my &quot;best&quot; checkpoint:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gold=A: 7/374   (1.9%)
gold=B: 0/813   (0.0%)
gold=C: 915/928 (98.6%)
gold=D: 0/385   (0.0%)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The model was not doing cybersecurity reasoning. It had collapsed onto the letter C and was scoring off the label skew. My 36.9% was a measurement of how unbalanced the benchmark was.&lt;/p&gt;
&lt;p&gt;The fix is to score the text of each option, not its letter. Instead of comparing log P(&quot;C&quot;), compare log P(option_text) under the prompt. Now the model has to actually prefer the correct statement, not the correct position.&lt;/p&gt;
&lt;h3 id=&quot;2-debias-with-multiple-permutations-&quot;&gt;2. Debias with multiple permutations.&lt;/h3&gt;
&lt;p&gt;Text scoring alone is not enough, because a model can still have a positional bias that text scoring does not fully remove. The clean test is to shuffle the option order.&lt;/p&gt;
&lt;p&gt;Run each question N times with the options in different orders, and require the model to pick the same correct answer every time. A model that is genuinely reasoning is invariant to order. A model that is secretly emitting one position collapses to random under permutation.&lt;/p&gt;
&lt;p&gt;I ran four permutations across all 2,500 questions on every checkpoint. The result was humbling. The checkpoint I had crowned as &quot;canonical&quot; at 36.9% single-order got zero questions correct under all four permutations. A different checkpoint I had dismissed as a failed run got three correct under all four. The single-order ranking was inverted from real capability.&lt;/p&gt;
&lt;p&gt;If a number does not survive permutation, it is not a capability, it is an artifact.&lt;/p&gt;
&lt;h3 id=&quot;3-for-classification-subtract-the-prior-pmi-scoring-&quot;&gt;3. For classification, subtract the prior (PMI scoring).&lt;/h3&gt;
&lt;p&gt;If your task is classification (pick the category, the severity, the technique), you will hit a related trap. Score by raw conditional probability and the model will assign its highest probability to the same common label for every input, regardless of the question. Every checkpoint I trained reported exactly the random baseline on my classification eval, which is the signature of a mode-collapsed metric, not a model that cannot learn.&lt;/p&gt;
&lt;p&gt;The fix is pointwise mutual information. Instead of scoring log P(label | context), subtract the model's unconditional preference for that label:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pmi_score = log P(label | context) - log P(label)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This cancels out the model's prior bias toward common labels, so a label only wins if the context actually raises its probability. After switching to PMI, my eval could finally tell checkpoints apart instead of flatlining at random.&lt;/p&gt;
&lt;h3 id=&quot;4-audit-your-train-validation-split-for-leakage-&quot;&gt;4. Audit your train/validation split for leakage.&lt;/h3&gt;
&lt;p&gt;Before you trust any validation number, check how you split the data. I split mine by random shuffle, which sounds fine until you remember that real corpora are repetitive. In my case CVE descriptions repeat the same patterns with slight wording changes, so a random split put near-duplicate records on both sides. The model memorized in training and &quot;validated&quot; on text it had effectively already seen.&lt;/p&gt;
&lt;p&gt;My leaky split reported a validation loss of 2.74. After I fixed it the same model on the same recipe reported 3.78. The model did not get worse. The eval got honest.&lt;/p&gt;
&lt;p&gt;The fix is deterministic content-hash bucketing, so identical or near-identical text always lands in the same split:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;bucket = int(hashlib.sha256(text.encode()).hexdigest(), 16) % 100
split = &quot;val&quot; if bucket &amp;lt; val_pct else &quot;train&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;5-audit-for-contamination-against-your-training-corpus-&quot;&gt;5. Audit for contamination against your training corpus.&lt;/h3&gt;
&lt;p&gt;A clean split protects you from leakage you created. Contamination is leakage you inherited, when your training corpus happens to overlap with the benchmark's sources.&lt;/p&gt;
&lt;p&gt;I checked this with an 8-gram shingle overlap between every benchmark question and my training data. Eleven percent of my benchmark questions had at least one overlap. Interestingly, the contaminated questions scored slightly worse than the clean ones, which told me the overlap was confusing the model rather than helping it, and ruled out the &quot;leakage inflated my score&quot; theory. Either way, you want to know the number before you report anything.&lt;/p&gt;
&lt;h3 id=&quot;6-look-at-per-source-perplexity-not-just-aggregate-loss-&quot;&gt;6. Look at per-source perplexity, not just aggregate loss.&lt;/h3&gt;
&lt;p&gt;Aggregate validation loss hides what the model is actually learning. When I broke perplexity out per source, I could see that one document type (short CVE descriptions, 87% of my tokens) was dominating, and the model had learned exactly one output register and applied it to everything. A CTF prompt produced fake CVE text. A research-paper prompt produced fake CVE text.&lt;/p&gt;
&lt;p&gt;A per-source breakdown after every run tells you whether new data is helping or quietly cannibalizing the domains you already had. When I added one large source and watched every other source get 28 to 42% worse, that was the model hitting its capacity ceiling, visible only because the metric was split by source.&lt;/p&gt;
&lt;h3 id=&quot;7-test-free-form-recall-not-just-multiple-choice-&quot;&gt;7. Test free-form recall, not just multiple choice.&lt;/h3&gt;
&lt;p&gt;Multiple choice flatters a model because the answer is on the screen. To find out whether the model actually knows anything, ask it open questions and grade by substring match, with disqualifier phrases to catch lucky echoes.&lt;/p&gt;
&lt;p&gt;I built a 50-question free-form recall set. The scores were brutal and honest: roughly one correct out of fifty, and even that one was spurious because the model echoed a term from the question. The conclusion was clear and worth more than any benchmark percentage. My models had absorbed the vocabulary and register of the domain but not the facts. They were fluent parrots. That is a useful thing to know about a small model, and multiple choice will never tell you.&lt;/p&gt;
&lt;h3 id=&quot;the-meta-lesson&quot;&gt;The meta-lesson&lt;/h3&gt;
&lt;p&gt;At small scale, benchmark quality compounds more than model quality. Every one of my real improvements came from fixing the eval, not the model. Fixing the leaky split alone changed a perplexity number by more than 14x with zero extra training. Fixing the letter-scoring bug revealed that months of ablations had been optimizing positional bias instead of capability.&lt;/p&gt;
&lt;p&gt;So the protocol, short version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Score option text, never the letter.&lt;/li&gt;
&lt;li&gt;Require the answer to survive multiple option permutations.&lt;/li&gt;
&lt;li&gt;Use PMI scoring for classification so common labels do not win by default.&lt;/li&gt;
&lt;li&gt;Split by content hash so the validation set is honest.&lt;/li&gt;
&lt;li&gt;Shingle-check the benchmark against your training corpus for contamination.&lt;/li&gt;
&lt;li&gt;Break perplexity out by source.&lt;/li&gt;
&lt;li&gt;Test free-form recall to separate register from knowledge.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this is expensive. The permutation test is about twenty lines of Python. The contamination check is similar. The reason these bugs survive long enough to get into published numbers is not that they are hard to catch, it is that people rarely audit the benchmark before trusting it. Audit it first.&lt;/p&gt;
&lt;p&gt;The full harness I built while finding these bugs is open source, including the multi-permutation text scorer, the free-form recall grader, and the contamination checker.&lt;/p&gt;
&lt;p&gt;GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/joemunene-by/GhostLM&quot;&gt;https://github.com/joemunene-by/GhostLM&lt;/a&gt;&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/20374/how-to-evaluate-a-small-language-model-on-multiple-choice-without-fooling-yourself</guid>
<pubDate>Fri, 12 Jun 2026 07:42:26 +0000</pubDate>
</item>
<item>
<title>I thought my LLM hit 36.9% on a cybersecurity benchmark. It was picking C every time.</title>
<link>https://coderlegion.com/17411/i-thought-my-llm-hit-36-9-on-a-cybersecurity-benchmark-it-was-picking-c-every-time</link>
<description>&lt;p&gt;I've been building GhostLM — a decoder-only transformer trained from scratch in PyTorch on cybersecurity data. No pretrained weights, every component hand-written. A few weeks ago I published a post about fixing a leaky train/val split that made my model look 14x better than it was.&lt;br&gt;
I just found a second bug. This one made the benchmark itself look meaningful.&lt;br&gt;
The 36.9% that wasn't&lt;br&gt;
After months of training, I had a chat-tuned model hitting 36.9% on CTIBench MCQ — a 2,500-question cybersecurity multiple-choice benchmark. That's above random (25%) and I was running ablations trying to reproduce and beat it.&lt;br&gt;
Then I checked the gold label distribution.&lt;br&gt;
gold=A:  374 (15.0%)&lt;br&gt;
gold=B:  813 (32.5%)&lt;br&gt;
gold=C:  928 (37.1%)  ← skewed&lt;br&gt;
gold=D:  385 (15.4%)&lt;br&gt;
A model that always picks C scores 37.1%. Higher than my &quot;best&quot; result.&lt;br&gt;
I ran a quick diagnostic on the canonical checkpoint. Per-letter accuracy:&lt;br&gt;
gold=A:    7/374  (1.9%)&lt;br&gt;
gold=B:    0/813  (0.0%)&lt;br&gt;
gold=C:  915/928  (98.6%)&lt;br&gt;
gold=D:    0/385  (0.0%)&lt;br&gt;
The model wasn't doing cybersecurity reasoning. It was emitting C on every question and scoring off the label distribution.&lt;br&gt;
The permutation test&lt;br&gt;
The correct eval is multi-permutation text scoring. Instead of scoring log P(&quot;C&quot; | prompt), score log P(option_text | prompt) under N different option orderings. A pure C-emitter collapses to 25% random under this metric regardless of label distribution.&lt;br&gt;
I ran 4 option permutations × 2,500 questions across every checkpoint:&lt;br&gt;
Modelsingle-orderdebiasedall-perm correctchat-v3 &quot;canonical&quot;36.9%30.5%0/2500chat-v3 repro231.2%31.7%3/2500chat-v0629.8%31.2%0/2500&lt;br&gt;
The model I'd dismissed as a &quot;failed reproduction&quot; (31.2% single-order) answered 3 questions correctly under all permutations. The &quot;canonical&quot; answered zero. The single-order ranking is inverted from real capability.&lt;br&gt;
I also found that the bias letter depends on the base checkpoint, not the SFT recipe. v0.4 base learned to emit C. v0.6 base learned to emit B. v0.6 hybrid learned to emit A and scored 37.1% under a different permutation — same trick, different letter.&lt;br&gt;
What the models actually know&lt;br&gt;
After switching to text scoring, every checkpoint clustered at 27-29% on the full 2,500-question bench — roughly 4 points above random. I also built a 50-question free-form fact recall set with substring grading:&lt;/p&gt;
&lt;p&gt;v0.4 chat: 0/50&lt;br&gt;
v0.7 chat: 1/50 (spurious — &quot;Injection&quot; appeared in tangent prose)&lt;br&gt;
v0.9 chat: 1/50 (spurious — echoed &quot;SHA-256&quot; from the question)&lt;/p&gt;
&lt;p&gt;The models have absorbed the register and vocabulary of cybersecurity writing. They do not know the facts. EternalBlue gets a wrong CVE. MITRE technique IDs get conflated. The model is a cybersec parrot.&lt;br&gt;
Contamination audit&lt;br&gt;
After discovering v0.9 was trained on PRIMUS corpus that overlaps with CTIBench sources, I ran an 8-gram shingle overlap check. 11% of CTIBench questions have at least one overlap with the training corpus. The contaminated questions score 2.2pp lower than clean ones — contamination is confusing the model, not helping it. This rules out the leakage-helps hypothesis and points to register-shift as the explanation.&lt;br&gt;
What I built while diagnosing this&lt;br&gt;
The diagnosis forced me to build better infrastructure:&lt;/p&gt;
&lt;p&gt;eval_text_scoring.py — multi-permutation text scoring, works on any MCQ JSONL&lt;br&gt;
eval_fact_recall_v2.py — free-form fact recall with boundary matching and disqualifier phrases&lt;br&gt;
audit_ctibench_contamination.py — shingle overlap checker&lt;br&gt;
GhostBench — a packaged eval suite with Wilson 95% CIs, McNemar's tests, forest plots, scaling-law projections. Can evaluate any small open LM, not just GhostLM.&lt;/p&gt;
&lt;p&gt;I also published the fact-recall benchmark as a HuggingFace dataset: Ghostgim/cybersec-fact-recall.&lt;br&gt;
The actual bottleneck&lt;br&gt;
The RAG diagnostic told the story clearly:&lt;br&gt;
MetricScoreRetrieval@4 (no LM)41/100v0.9 bare1/100v0.9 + RAG0/100&lt;br&gt;
The retriever surfaces the right passage 41% of the time. The 81M model extracts the fact 1% of the time. Adding retrieved context destabilizes the model into repetition loops. The bottleneck is generation capacity, not retrieval. Parameter scaling is the answer.&lt;br&gt;
SmolLM2-360M and Phi-3.5-mini both show factual recall emerging around 300-400M params. Ghost-base (~360M) is the next rung, gated on GPU compute.&lt;br&gt;
What I actually learned&lt;br&gt;
Benchmark quality compounds more than model quality at small scale. Fixing the eval methodology revealed I'd been chasing a number that was measuring positional bias, not capability. Months of ablations were optimizing the wrong objective.&lt;br&gt;
The correct eval protocol for MCQ at small scale: text scoring + multi-permutation debiasing. Single-order letter scoring is uninterpretable on any benchmark with skewed gold label distribution.&lt;br&gt;
The 30% real ceiling across every architecture, BPE, SFT objective, and corpus density I tried is the clearest result I have. It's reproducible, it's controlled, and it tells me exactly what the next experiment needs to be.&lt;br&gt;
GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/joemunene-by/GhostLM&quot;&gt;https://github.com/joemunene-by/GhostLM&lt;/a&gt;&lt;br&gt;
Eval harness: scripts/eval_text_scoring.py&lt;br&gt;
Fact-recall dataset: huggingface.co/datasets/Ghostgim/cybersec-fact-recall&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/17411/i-thought-my-llm-hit-36-9-on-a-cybersecurity-benchmark-it-was-picking-c-every-time</guid>
<pubDate>Mon, 11 May 2026 08:17:18 +0000</pubDate>
</item>
<item>
<title>Building &amp; Staying Secure as a Solo Developer (Text Seminar)</title>
<link>https://coderlegion.com/9543/building-staying-secure-as-a-solo-developer-text-seminar</link>
<description>&lt;p&gt;This post is written in a &lt;strong&gt;seminar-style format&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You can read it quietly, pause, reflect, and come back.&lt;br&gt;
No rush. No performance.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;welcome&quot;&gt;Welcome&lt;/h3&gt;
&lt;p&gt;Hi, I’m &lt;strong&gt;Prasoon Jadon&lt;/strong&gt;, a solo builder working on tools, open-source projects, and security awareness under &lt;strong&gt;Vyoma&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This article is part of &lt;strong&gt;VSAP — Vyoma Security Awareness Program&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The goal is simple:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Help developers stay secure while building publicly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-this-matters&quot;&gt;Why This Matters&lt;/h3&gt;
&lt;p&gt;As developers, we:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Push code publicly&lt;/li&gt;
&lt;li&gt;Share progress online&lt;/li&gt;
&lt;li&gt;Build in open communities&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is powerful — but it also creates &lt;strong&gt;attack surfaces&lt;/strong&gt; we often ignore.&lt;/p&gt;
&lt;p&gt;Security is not paranoia.&lt;br&gt;
Security is awareness.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-public-repositories-are-not-safe-by-default&quot;&gt;1️⃣ Public Repositories Are Not Safe by Default&lt;/h3&gt;
&lt;p&gt;One of the most common mistakes I see:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Treating public repositories as harmless.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Never commit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API keys&lt;/li&gt;
&lt;li&gt;Tokens&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.env&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;Internal URLs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even if you delete them later, &lt;strong&gt;history remains&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Habit to build:&lt;/strong&gt;&lt;br&gt;
Always assume your code will be read by strangers.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-osint-works-both-ways&quot;&gt;2️⃣ OSINT Works Both Ways&lt;/h3&gt;
&lt;p&gt;OSINT (Open-Source Intelligence) means:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Collecting information from publicly available sources.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Developers unknowingly leak:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Email addresses&lt;/li&gt;
&lt;li&gt;GitHub activity patterns&lt;/li&gt;
&lt;li&gt;Tech stack details&lt;/li&gt;
&lt;li&gt;Location hints&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you can research companies using OSINT,&lt;br&gt;
someone can research &lt;strong&gt;you&lt;/strong&gt; the same way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Question to reflect on:&lt;/strong&gt;&lt;br&gt;
What can someone learn about me in 10 minutes online?&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-security-is-a-habit-not-a-tool&quot;&gt;3️⃣ Security Is a Habit, Not a Tool&lt;/h3&gt;
&lt;p&gt;Many developers ask:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“Which security tool should I use?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The honest answer:&lt;br&gt;
Tools help, but &lt;strong&gt;habits protect&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Core habits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unique passwords&lt;/li&gt;
&lt;li&gt;Password managers&lt;/li&gt;
&lt;li&gt;Regular updates&lt;/li&gt;
&lt;li&gt;Minimal permissions&lt;/li&gt;
&lt;li&gt;Thinking before sharing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most breaches happen due to &lt;strong&gt;human habits&lt;/strong&gt;, not missing tools.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-building-in-public-oversharing&quot;&gt;4️⃣ Building in Public ≠ Oversharing&lt;/h3&gt;
&lt;p&gt;Building in public is great.&lt;br&gt;
Oversharing is risky.&lt;/p&gt;
&lt;p&gt;You don’t need to share:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Exact infrastructure details&lt;/li&gt;
&lt;li&gt;Internal logic&lt;/li&gt;
&lt;li&gt;Sensitive architecture diagrams&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt;&lt;br&gt;
Share learnings, not weaknesses.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-calm-security-gt-fear-based-security&quot;&gt;5️⃣ Calm Security &amp;gt; Fear-Based Security&lt;/h3&gt;
&lt;p&gt;Security content often relies on fear.&lt;/p&gt;
&lt;p&gt;VSAP focuses on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Awareness over panic&lt;/li&gt;
&lt;li&gt;Habits over hacks&lt;/li&gt;
&lt;li&gt;Calm learning&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Security should empower you, not scare you.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;open-reflection&quot;&gt;Open Reflection&lt;/h3&gt;
&lt;p&gt;If this were a live text seminar, I’d ask:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What security mistake taught you the most?&lt;/li&gt;
&lt;li&gt;What’s one habit you want to improve?&lt;/li&gt;
&lt;li&gt;What topic should the next session cover?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can reflect privately or share in comments.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;closing-thoughts&quot;&gt;Closing Thoughts&lt;/h3&gt;
&lt;p&gt;This article is not about being perfect.&lt;br&gt;
It’s about being &lt;strong&gt;a little more aware than yesterday&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I’m experimenting with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Text-based seminars&lt;/li&gt;
&lt;li&gt;Calm developer communities&lt;/li&gt;
&lt;li&gt;Community-first growth under Vyoma&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If this style resonates, more sessions will follow.&lt;/p&gt;
&lt;p&gt;Thanks for reading quietly &lt;br&gt;
— &lt;strong&gt;Prasoon Jadon&lt;/strong&gt;&lt;br&gt;
Founder, Vyoma&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/9543/building-staying-secure-as-a-solo-developer-text-seminar</guid>
<pubDate>Sun, 11 Jan 2026 13:44:59 +0000</pubDate>
</item>
<item>
<title>How We Solved “Unable to Get Certificate CRL” in Rails: A Debugging Story</title>
<link>https://coderlegion.com/7310/how-we-solved-unable-to-get-certificate-crl-in-rails-a-debugging-story</link>
<description>&lt;p&gt;If you’ve been around Rails long enough, you’ve probably battled your fair share of SSL demons. But this one? This one had &lt;em&gt;personality&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;It was a &lt;code&gt;Seahorse::Client::NetworkingError&lt;/code&gt; that failed &lt;strong&gt;only on macOS&lt;/strong&gt; — yet worked perfectly inside Docker. The kind of “it works on my container” bug that makes you question every life choice leading up to this point.&lt;/p&gt;
&lt;p&gt;So here’s the story of how we chased down a ghostly SSL error, only to discover that the real culprit was… OpenSSL itself.&lt;/p&gt;
&lt;h2&gt;⚠️ The Error&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Seahorse::Client::NetworkingError
SSL_connect returned=1 errno=0 peeraddr=[...]
state=error: certificate verify failed (unable to get certificate CRL)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This popped up whenever our Rails app tried connecting to AWS S3 using the &lt;code&gt;aws-sdk-ruby&lt;/code&gt; gem.&lt;/p&gt;
&lt;h2&gt; The Contradiction That Made No Sense&lt;/h2&gt;
&lt;p&gt;From the get-go, the bug refused to play by any rules of logic.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;❌ &lt;strong&gt;It failed locally&lt;/strong&gt; but&lt;/li&gt;
  &lt;li&gt;✅ &lt;strong&gt;Worked flawlessly in Docker&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That told us one thing: the problem wasn’t our code. It was the environment. macOS and Docker’s Linux setup were behaving differently.&lt;/p&gt;
&lt;h2&gt; The “Wait, What?” Moment&lt;/h2&gt;
&lt;p&gt;We decided to test the SSL connection manually, expecting it to fail the same way:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openssl s_client -connect 52.219.66.109:443 -servername s3.ap-south-1.amazonaws.com &amp;lt; /dev/null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Result?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Verify return code: 0 (ok)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, macOS’s OpenSSL tool had no problem verifying the certificate.  
But Rails (and the AWS SDK) still choked on it.&lt;/p&gt;
&lt;p&gt;At this point, confusion levels were high. Coffee consumption was higher.&lt;/p&gt;
&lt;h2&gt; The Real Test — Inside the Rails Console&lt;/h2&gt;
&lt;p&gt;To rule out any system-level differences, we went straight into &lt;code&gt;rails c&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;require 'aws-sdk-s3'
s3 = Aws::S3::Client.new(region: 'ap-south-1')
s3.list_buckets
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;certificate verify failed (unable to get certificate CRL)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Boom. So the system OpenSSL was fine, but Ruby’s built-in OpenSSL (via &lt;code&gt;Net::HTTP&lt;/code&gt;) was throwing a tantrum.&lt;/p&gt;
&lt;h2&gt; The Root Cause — A Policy Change in OpenSSL&lt;/h2&gt;
&lt;p&gt;That cryptic message — &lt;em&gt;“unable to get certificate CRL”&lt;/em&gt; — turned out to be the key.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;CRL (Certificate Revocation List)&lt;/strong&gt; is basically a “blacklist” for certificates that have been revoked. The problem? The AWS certificate didn’t actually &lt;em&gt;have&lt;/em&gt; a CRL defined.&lt;/p&gt;
&lt;p&gt;We checked with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openssl s_client ... | openssl x509 -noout -text | grep &quot;CRL Distribution Points&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;X509v3 CRL Distribution Points:
# (empty)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Comparing environments made things click:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Environment&lt;/th&gt;
      &lt;th&gt;OpenSSL Version&lt;/th&gt;
      &lt;th&gt;Result&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;macOS (failing)&lt;/td&gt;
      &lt;td&gt;3.4.0 (new)&lt;/td&gt;
      &lt;td&gt;❌ Fails&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Docker (working)&lt;/td&gt;
      &lt;td&gt;3.0.11 (stable)&lt;/td&gt;
      &lt;td&gt;✅ Works&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Turns out, &lt;strong&gt;OpenSSL 3.4.0 introduced stricter validation rules&lt;/strong&gt;.  
It now treats missing CRL entries as fatal errors — whereas the 3.0.x series simply shrugged and moved on.&lt;/p&gt;
&lt;p&gt;So no, your Mac isn’t broken — it’s just being &lt;em&gt;too helpful&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt; The Fixes — Two Paths to Sanity&lt;/h2&gt;
&lt;p&gt;After a lot of trial (and some error), we landed on two solid fixes.&lt;/p&gt;
&lt;h3&gt; Fix 1: The Local-Only (Insecure but Practical) Workaround&lt;/h3&gt;
&lt;p&gt;Since this issue only showed up in development, one quick way to move on is to tell AWS SDK to skip SSL verification locally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create:&lt;/strong&gt; &lt;code&gt;config/initializers/aws_ssl_workaround.rb&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# WARNING: LOCAL DEVELOPMENT ONLY
# OpenSSL 3.4.0 enforces stricter CRL policies, so skip verification here.
if Rails.env.development?
  Aws.config.update(ssl_verify_peer: false)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;✅ Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Works instantly&lt;/li&gt;
  &lt;li&gt;Doesn’t affect production&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Insecure (but fine for local development)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt; Fix 2: Match Environments (I tried this one!)&lt;/h3&gt;
&lt;p&gt;The better long-term solution is to make your local OpenSSL match Docker’s version.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Install an older OpenSSL (3.0.x):&lt;/strong&gt;
    &lt;pre&gt;&lt;code&gt;brew install openssl@3.0&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;

  &lt;li&gt;&lt;strong&gt;Reinstall Ruby:&lt;/strong&gt;
    &lt;pre&gt;&lt;code&gt;rbenv uninstall 3.2.2
RUBY_CONFIGURE_OPTS=&quot;--with-openssl-dir=$(brew --prefix openssl@3.0)&quot; rbenv install 3.2.2&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;

  &lt;li&gt;&lt;strong&gt;Reinstall gems:&lt;/strong&gt;
    &lt;pre&gt;&lt;code&gt;gem install bundler
bundle install&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now your local Ruby is linked against OpenSSL 3.0 — just like Docker and peace is restored.&lt;/p&gt;
&lt;h2&gt; Confirmation &amp;amp; References&lt;/h2&gt;
&lt;p&gt;Turns out, we weren’t the only ones haunted by this ghost:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/openssl/openssl/issues/28758&quot; target=&quot;_blank&quot;&gt;openssl/openssl#28758&lt;/a&gt; — CRL verification failures in OpenSSL 3.6.0&lt;/li&gt;
  &lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/ruby/openssl/issues/949&quot; target=&quot;_blank&quot;&gt;ruby/openssl#949&lt;/a&gt; — Net::HTTP failing due to stricter CRL rules&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt; Takeaway&lt;/h2&gt;
&lt;p&gt;SSL errors can feel like black magic, but more often than not, they’re &lt;em&gt;version mismatches in disguise&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So next time something works in Docker but not on your laptop, remember:  
sometimes it’s not your code, it’s just your OpenSSL being extra.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Thanks for reading! If you found this helpful, share it with a fellow developer who’s currently staring at an SSL error and losing their mind.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Backend Dev</category>
<guid isPermaLink="true">https://coderlegion.com/7310/how-we-solved-unable-to-get-certificate-crl-in-rails-a-debugging-story</guid>
<pubDate>Thu, 13 Nov 2025 11:53:55 +0000</pubDate>
</item>
<item>
<title>How a VPN Works: A Technical Mind Map</title>
<link>https://coderlegion.com/6433/how-a-vpn-works-a-technical-mind-map</link>
<description>&lt;p&gt;TLDR; client -&amp;gt; server -&amp;gt; internet -&amp;gt; server -&amp;gt; client&lt;/p&gt;
&lt;p&gt;A while ago a classmate told me he had built a VPN in Java for his final project and I found it very interesting. Too interesting in fact to leave the idea alone.&lt;/p&gt;
&lt;p&gt;I found that I really didn't know what a VPN really was even though I had used it so many times. So this article is a technical mind map of what I found. I will be exposing my approach to thinking about the technology itself and the questions I asked myself. Right now, there are two layers of understanding to this for me. I will update these layers when I found out more things. The first layer is the conceptual overview of how a VPN works. The second layer is more architectural, so if you wanted to build one yourself how would you do it? That's the question the second layer answers. So let's dive right in.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-conceptual-overview-what-is-a-vpn-&quot;&gt;The Conceptual Overview: What is a VPN?&lt;/h3&gt;
&lt;p&gt;A VPN stands for &lt;strong&gt;Virtual Private Network&lt;/strong&gt;. Taking each word apart from the rear, we can easily tell there's network engineering involved, then some cybersecurity, and finally, it's &lt;strong&gt;virtualized&lt;/strong&gt; as in a logical rather than physical thing. In software engineering, 'virtualized' generally means it's replicating some hardware function on software. So what does it do really?&lt;/p&gt;
&lt;p&gt;Simply put, it allows you to access a network (whether LAN or WAN) &lt;strong&gt;securely&lt;/strong&gt; over the internet. &lt;strong&gt;Access&lt;/strong&gt; and &lt;strong&gt;Securely&lt;/strong&gt; being the two most important words there. The typical example you'd find is of an office. Let's assume this office runs an intranet, so things like the official communication channel, files, etc., are hosted on a physical server that is within the office premises. Whenever you're in the office, you can access these services just by typing the IP address (or domain name) in your browser or looking through the networks tab of your file explorer. This is because you are connected to the very same network as the servers that host these things and you probably have network discovery turned on on your work device.&lt;/p&gt;
&lt;p&gt;Now whenever you are outside the office, you cannot access these services and documents because you are no longer within the same local network. But if your office connects these servers to the internet, and you are also connected to the internet where you are, then by design you should be able to make a request across the internet to your office servers and they should be able to respond. So what's the problem here? Well, if it's over the public internet that you make these requests, then &lt;em&gt;technically&lt;/em&gt; it's open for everyone to see, and your work information flying over the airways can be sniffed and potentially stolen. By extension, the data could have easily been your banking information, passwords, personal identification details, and other very important and very personal data. A solution to this is a VPN. This brings us to the question the first layer answers, which is, aside from the acronym, what is a VPN?&lt;/p&gt;
&lt;p&gt;If you google this question, you'd most likely see something along the lines of a VPN is a secure encrypted tunnel that protects your internet traffic, masks your IP address, and some other amazing things. So following from the scenario above, when you turn on your VPN on your work device, your internet traffic is suddenly no more open, visible, or accessible to the public internet. It encrypts your traffic from your device, and when it gets to the &lt;strong&gt;VPN server&lt;/strong&gt;, it is decrypted and then forwarded to the internet or the services you want to access.&lt;/p&gt;
&lt;p&gt;This VPN tunnel would have two parts: the &lt;strong&gt;VPN client&lt;/strong&gt; on your device and the &lt;strong&gt;VPN server&lt;/strong&gt; at the other end. The server waits for connections from the client so that it can make requests to the internet on behalf of the client and then send the results back. As a side effect, your IP address is also changed, which makes it look like you're in another country. If your requests/internet traffic is not encrypted (and tunnelled) then this is merely a proxy server. You request is just bouncing off of another server before it goes to the internet. But when you have a proxy plus encryption (and tunnelling) then congrats, you have a VPN.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-architectural-layer-building-your-own-vpn&quot;&gt;The Architectural Layer: Building Your Own VPN&lt;/h3&gt;
&lt;p&gt;Now on to the second question: if I wanted to build my own VPN, how would I do it? You might think it's difficult on the surface, and you would be right to think that, but once you get the core things figured out, the components stack nicely on top of each other to give you the desired result. At this point, you already know you would need two things: the VPN server and the VPN client. These two things are software you can write in any programming language of your choice.&lt;/p&gt;
&lt;p&gt;A VPN has three core components and one extra component that wraps everything together:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;Virtual Network Adapter (the tunnel)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transport&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encryption&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The last thing that wraps everything together are just the headaches (essential security and management mechanisms) that keep the whole thing super secure, like auth handshakes, pre-shared keys, keep-alive requests, session management etc. These things are peripherals but they help run a tight ship in terms of a VPN service. We would start building from the perspective of the VPN client. So let's break it down.&lt;/p&gt;
&lt;h4 id=&quot;the-virtual-network-adapter-tun-tap-&quot;&gt;The Virtual Network Adapter (TUN/TAP)&lt;/h4&gt;
&lt;p&gt;I'll explain it this way: growing up, I used to play Blur on LAN parties with my friends. We connected everyone with ethernet cables and could share files because we were all connected to the same network. This kind of connection is made when you plug an ethernet cable into your computer. The cable speaks to the &lt;strong&gt;Network Adapter&lt;/strong&gt; hardware, your NIC (Network Interface Card).&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;virtual network adapter&lt;/strong&gt; is software that mimics the behaviour of being physically plugged in to a network. Note that when you're plugged into a network, all the requests you make go through your physical network adapter. This is the exact same concept with your virtual network adapter, but you can choose what kind of traffic passes through it. It could be all internet traffic or just some.&lt;/p&gt;
&lt;p&gt;In a bit more detail, when you turn on the VPN service, it creates this virtual network adapter, which can be read and written to like a file descriptor. You will find it referred to as a &lt;strong&gt;TUN/TAP interface&lt;/strong&gt;. If you choose for all internet traffic to go to this virtual interface then when you request for a page in your browser, the browser passes the request to the OS to pass to it's network stack but then the OS sees your virtual network adapter and then writes the users request to your virtual network adapter instead. So in your VPN program you can read this request and chose what to do with it. This has now gotten us to the transport section.&lt;/p&gt;
&lt;h4 id=&quot;transport-encryption-and-encapsulation&quot;&gt;Transport, Encryption, and Encapsulation&lt;/h4&gt;
&lt;p&gt;VPNs usually send encrypted traffic over the internet and there are a number of protocols by which devices talk to one another over the internet. You need to pick one to use (heck you could write your own protocol too!). But generally speaking, TCP and UDP are common transport protocols when it comes to transport with VPNs. Both come with their own headaches so research the best one for your use case. I particularly like UDP because of its statelessness which could be a pro or a con depending on how you look at it.&lt;/p&gt;
&lt;p&gt;The transport effectively establishes a way to communicate with the VPN server so you can send the user's request that you have read from your virtual network adapter.&lt;/p&gt;
&lt;p&gt;Before you send the request, you need to process it. This is where the &lt;strong&gt;encryption and encapsulation&lt;/strong&gt; come in. You can encrypt the entire package with pre-shared keys or whatever type of encryption you deem fit. The encapsulation typically includes slapping on some new request headers to the encrypted data.&lt;/p&gt;
&lt;h4 id=&quot;the-vpn-server&quot;&gt;The VPN Server&lt;/h4&gt;
&lt;p&gt;Okay, the VPN client is able to send encrypted packets over to the server, so now we shift our focus to the server.&lt;/p&gt;
&lt;p&gt;The first thing we want to do is to setup our transport in order to receive the packets. If you used UDP on the client then you'd need to setup a UDP server on the VPN server so that the UDP client on the VPN client can connect to it and send those packets in the first place. You might decide to use TCP and the flow would be the exact same. There has to be a way for the packets to reach the server because this is your entry point.&lt;/p&gt;
&lt;p&gt;At this point you might want to setup some of those headaches that we talked about previously so that not just anybody can connect to your server. Establishing a proper peer system would do some good here so only verified peers can send requests in the first place. Every other type of request should be ignored. Your VPN server itself should be a secure machine &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.victorabuka.com/posts/secure-a-vps-for-claude-code/&quot;&gt;(you can check here to see steps on how to get to the minimum security any server should have)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After receiving the packets securely, the next step would be to decrypt them and forward them to the internet. You already know what we're about to do here. If you guessed that we would create a virtual network adapter (TUN/TAP) on the VPN server then you are 100% correct. We do this because the same way we can read from it on the client we can write to it so that the OS picks it up and sends it to its networking stack and makes the request for us.&lt;/p&gt;
&lt;p&gt;When the OS gets the response from the internet, it writes the packets back to the virtual network adapter and we can read this response and send it back the way the request came. Finally the VPN client sees this response from the VPN server and sends it to its own virtual network adapter on the client device which then passes it to the user's OS and finally the OS passes it to the browser and the user sees the response.&lt;/p&gt;
&lt;p&gt;Congratulations you now have a VPN, it seems like a long journey but it happens pretty fast (when implemented without unnecessary latency from bad code). Now since the real device that makes the request to the internet is the VPN server this whole concept has the effect of masking a user's real IP address. If you are thinking of implementing a VPN you should really checkout the WireGuard protocol. VPN protocols are opinionated ways of doing this whole thing, they give you a recipe to follow for the primary steps and even the headaches (sessions/auth/handshakes, etc).&lt;/p&gt;
&lt;p&gt;This is my current mind map of how a VPN works on a technical level. I am open to comments, contributions, or questions.&lt;/p&gt;
&lt;p&gt;Cover Photo Credit: Illustration by &lt;a rel=&quot;nofollow&quot; href=&quot;https://unsplash.com/@slimanekadi?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Slimane-Kadi&lt;/a&gt; on &lt;a rel=&quot;nofollow&quot; href=&quot;https://unsplash.com/illustrations/abstract-swirling-colors-with-white-lines-wk_ss6kf3Vg?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
</description>
<category>Data Privacy</category>
<guid isPermaLink="true">https://coderlegion.com/6433/how-a-vpn-works-a-technical-mind-map</guid>
<pubDate>Tue, 14 Oct 2025 11:08:24 +0000</pubDate>
</item>
<item>
<title>67% of zero trust implementations fail due to partial zero trust execution.</title>
<link>https://coderlegion.com/4749/67-of-zero-trust-implementations-fail-due-to-partial-zero-trust-execution</link>
<description>&lt;h2 id=&quot;zero-trust-reality-check-why-implementation-failures-are-creating-false-security&quot;&gt;Zero Trust Reality Check: Why Implementation Failures Are Creating False Security&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Meta Description (150 chars):&lt;/strong&gt; 67% of zero trust implementations fail due to incomplete execution. Learn why partial zero trust creates more vulnerabilities than traditional security.&lt;/p&gt;
&lt;p&gt;Zero Trust has become the cybersecurity equivalent of a miracle diet—everyone claims to be doing it, but most implementations fall short of delivering promised results. Research presented at Black Hat 2025 reveals that incomplete Zero Trust deployments often create more vulnerabilities than the traditional perimeter-based security they're meant to replace.&lt;/p&gt;
&lt;h3 id=&quot;the-implementation-gap&quot;&gt;The Implementation Gap&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/chuck-h-securityexecutive/&quot;&gt;Chuck Herrin&lt;/a&gt;, Field CTO at &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.f5.com/&quot;&gt;F5&lt;/a&gt;, shared during Black Hat that he has observed this pattern across hundreds of enterprise deployments: &quot;Organizations think they're doing Zero Trust because they've implemented multifactor authentication and network segmentation. But Zero Trust is about complete verification of every transaction, not just adding more security layers.&quot;&lt;/p&gt;
&lt;p&gt;F5's research across their customer base shows that 67% of Zero Trust initiatives fail to achieve their intended security outcomes due to incomplete implementation, creating what Herrin calls &quot;Zero Trust theater.&quot;&lt;/p&gt;
&lt;h3 id=&quot;the-checkbox-zero-trust-problem&quot;&gt;The &quot;Checkbox Zero Trust&quot; Problem&lt;/h3&gt;
&lt;p&gt;Many organizations approach Zero Trust as a compliance exercise rather than a fundamental security transformation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Partial MFA Deployment&lt;/strong&gt;: Implementing multifactor authentication for some applications while leaving others unprotected&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Incomplete Network Segmentation&lt;/strong&gt;: Creating isolated network zones without proper access controls between them&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Identity Washing&lt;/strong&gt;: Rebranding existing identity management solutions as &quot;Zero Trust&quot; without architectural changes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vendor Confusion&lt;/strong&gt;: Believing that purchasing &quot;Zero Trust&quot; products automatically delivers Zero Trust architecture&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-false-security-paradox&quot;&gt;The False Security Paradox&lt;/h3&gt;
&lt;p&gt;Incomplete Zero Trust implementations can actually increase risk by creating a false sense of security. Herrin explained: &quot;When you implement pieces of Zero Trust without the full architecture, you often create new attack paths while believing you've eliminated old ones.&quot;&lt;/p&gt;
&lt;p&gt;Common failure patterns include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Trust Assumptions&lt;/strong&gt;: Assuming that verified identities don't need continuous monitoring&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network Blind Spots&lt;/strong&gt;: Creating secure zones that lack internal visibility&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Application Gaps&lt;/strong&gt;: Protecting modern applications while leaving legacy systems exposed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Device Inconsistency&lt;/strong&gt;: Managing corporate devices while ignoring BYOD risks&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-continuous-verification-challenge&quot;&gt;The Continuous Verification Challenge&lt;/h3&gt;
&lt;p&gt;True Zero Trust requires continuous verification of every transaction, but most organizations struggle with the operational overhead this creates. F5's customer data shows that organizations successfully implementing Zero Trust typically see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;40% increase in initial security operations workload&lt;/li&gt;
&lt;li&gt;60% reduction in successful breach attempts&lt;/li&gt;
&lt;li&gt;25% improvement in incident response times after full implementation&lt;/li&gt;
&lt;li&gt;35% reduction in overall security management complexity once mature&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;ai-as-a-zero-trust-accelerator&quot;&gt;AI as a Zero Trust Accelerator&lt;/h3&gt;
&lt;p&gt;Artificial intelligence is emerging as a critical enabler for practical Zero Trust implementation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Automated Policy Generation&lt;/strong&gt;: AI can analyze traffic patterns and automatically generate appropriate access policies&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Behavioral Analysis&lt;/strong&gt;: Machine learning models can establish baselines and identify anomalous behavior requiring additional verification&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Risk-Based Authentication&lt;/strong&gt;: AI can dynamically adjust authentication requirements based on real-time risk assessment&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuous Monitoring&lt;/strong&gt;: AI enables the constant verification that Zero Trust requires without overwhelming security teams&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-platform-approach&quot;&gt;The Platform Approach&lt;/h3&gt;
&lt;p&gt;Herrin advocates for platform-based Zero Trust implementation rather than point solution assembly: &quot;Organizations that succeed with Zero Trust treat it as an architectural transformation, not a product deployment. They build platforms that enable Zero Trust principles rather than buying Zero Trust products.&quot;&lt;/p&gt;
&lt;p&gt;F5's approach focuses on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Application-Centric Security&lt;/strong&gt;: Protecting applications regardless of where they're deployed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Identity-Aware Networking&lt;/strong&gt;: Making every network decision based on verified identity&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuous Risk Assessment&lt;/strong&gt;: Dynamically adjusting security controls based on real-time risk&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simplified Operations&lt;/strong&gt;: Reducing complexity through automation and integration&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;real-world-success-patterns&quot;&gt;Real-World Success Patterns&lt;/h3&gt;
&lt;p&gt;Organizations successfully implementing Zero Trust typically follow a phased approach:&lt;/p&gt;
&lt;h4 id=&quot;phase-1-identity-foundation-3-6-months-&quot;&gt;Phase 1: Identity Foundation (3-6 months)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Implement comprehensive identity management&lt;/li&gt;
&lt;li&gt;Deploy MFA across all applications&lt;/li&gt;
&lt;li&gt;Establish identity governance processes&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;phase-2-network-transformation-6-12-months-&quot;&gt;Phase 2: Network Transformation (6-12 months)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Implement micro-segmentation&lt;/li&gt;
&lt;li&gt;Deploy identity-aware network controls&lt;/li&gt;
&lt;li&gt;Eliminate VPN dependencies&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;phase-3-application-protection-12-18-months-&quot;&gt;Phase 3: Application Protection (12-18 months)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Protect all applications with identity-aware proxies&lt;/li&gt;
&lt;li&gt;Implement continuous application monitoring&lt;/li&gt;
&lt;li&gt;Deploy adaptive access controls&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;phase-4-continuous-optimization-ongoing-&quot;&gt;Phase 4: Continuous Optimization (Ongoing)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Leverage AI for automated policy optimization&lt;/li&gt;
&lt;li&gt;Implement advanced behavioral analytics&lt;/li&gt;
&lt;li&gt;Continuously refine security postures&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;common-implementation-mistakes&quot;&gt;Common Implementation Mistakes&lt;/h3&gt;
&lt;p&gt;Herrin has identified recurring patterns in failed Zero Trust implementations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Technology-First Approach&lt;/strong&gt;: Focusing on tools rather than architecture&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Incomplete Discovery&lt;/strong&gt;: Not understanding all assets and data flows before implementation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User Experience Neglect&lt;/strong&gt;: Implementing security that significantly impacts productivity&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vendor Lock-In&lt;/strong&gt;: Choosing solutions that don't integrate with existing infrastructure&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Training Inadequacy&lt;/strong&gt;: Failing to prepare security teams for operational changes&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;the-business-case-for-complete-zero-trust&quot;&gt;The Business Case for Complete Zero Trust&lt;/h3&gt;
&lt;p&gt;Organizations with mature Zero Trust implementations report significant business benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reduced Breach Impact&lt;/strong&gt;: Average breach costs drop by 45% compared to traditional security&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Improved Compliance&lt;/strong&gt;: Simplified audit processes and continuous compliance validation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced Productivity&lt;/strong&gt;: Secure access to applications from any location or device&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Operational Efficiency&lt;/strong&gt;: Reduced security management overhead through automation&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;strategic-recommendations&quot;&gt;Strategic Recommendations&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start with Architecture&lt;/strong&gt;: Design Zero Trust architecture before selecting technologies&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prioritize User Experience&lt;/strong&gt;: Ensure security improvements don't hinder productivity&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leverage AI&lt;/strong&gt;: Use artificial intelligence to manage the complexity of continuous verification&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plan for Integration&lt;/strong&gt;: Choose solutions that work with existing infrastructure&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Invest in Training&lt;/strong&gt;: Prepare security teams for operational changes&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;the-future-of-zero-trust&quot;&gt;The Future of Zero Trust&lt;/h3&gt;
&lt;p&gt;As cyber threats continue evolving, Zero Trust will become less of a strategic choice and more of a business necessity. Organizations that implement comprehensive Zero Trust architectures will be better positioned to handle sophisticated attacks, while those pursuing incomplete implementations will face increasing risks.&lt;/p&gt;
&lt;p&gt;Herrin's conclusion resonates throughout the industry: &quot;Zero Trust isn't a destination—it's a continuous journey of verification and improvement. Organizations that understand this will thrive. Those that treat it as a checkbox exercise will fail.&quot;&lt;/p&gt;
</description>
<category>Cybersecurity</category>
<guid isPermaLink="true">https://coderlegion.com/4749/67-of-zero-trust-implementations-fail-due-to-partial-zero-trust-execution</guid>
<pubDate>Fri, 15 Aug 2025 20:27:00 +0000</pubDate>
</item>
</channel>
</rss>