Bitware Labs Est. 2022

Bitware Labs / Notebook

Building is easy. Making it secure is something else


Give anyone thirty minutes in a modern AI code editor and they can produce a complete web application. Dark mode toggle, clean CSS animations, database schemas, Stripe checkout, and a polished administrative dashboard. The code compiles without errors. They push to GitHub, click deploy, and receive a live public URL.

The feeling is intoxicating. You feel like a full-stack systems architect who built an entire software business before lunch.

Building an app has become easy. Making it secure enough to survive on the public internet is something else entirely.

Language models build for the happy path. When prompted, an AI optimizes for immediate completion: how to make the code compile and render cleanly when you click around in Chrome. It does not build with hostility as a foundational premise. By default, the software an AI generates is nowhere near secure enough to be exposed to the public internet; it is an open invitation to the first automated probe that glances its way.

The catalogue of vibecoded leaks

These breaches are rarely the result of novel cryptographic weaknesses or sophisticated zero-day exploits. They stem from the exact same handful of catastrophic oversights, repeated across app after app, because the model fundamentally lacks an operational understanding of operating systems, network perimeters, and trust boundaries.

Here is what is being pushed to production every day:

  • The exposed .env file in the web root. The AI generates a minimal nginx or reverse proxy configuration to serve static assets. Nobody added a location block to deny hidden dotfiles. A simple curl https://app.example.com/.env returns the DATABASE_URL, JWT_SECRET, AWS_SECRET_ACCESS_KEY, and STRIPE_SECRET_KEY in plain text. No firewall triggers. No alerts fire. The credentials are simply gone.
  • Secrets bundled into client-side JavaScript. The developer prompts the model to call an AI provider or billing API from a React or Vue component. The model obliges by prefixing the secret with NEXT_PUBLIC_ or inlining the API key straight into the Vite bundle. To the vibecoder, the UI works. To the rest of the world, their live API keys are sitting inside a publicly downloadable chunk file in /assets/.
  • Databases listening on 0.0.0.0. A standard docker-compose.yml exposing "5432:5432" or MongoDB on its default port. On Linux, Docker manipulates iptables directly, quietly bypassing UFW firewall rules. The database is bound directly to the public network interface, frequently with default credentials or a trivial password.
  • Row Level Security disabled for convenience. During development against Supabase or Firebase, the developer runs into a 403 Forbidden error. They paste the error into their chat window and ask how to get past it. The AI helpfully suggests running ALTER TABLE users DISABLE ROW LEVEL SECURITY; or provides a catch-all policy that returns true for all operations. The friction disappears, the demo works, and the entire database is now readable by anyone with a command line.
  • Insecure Direct Object References (IDOR). An endpoint like /api/invoices/[id] queries the database for the given invoice. The model generates the lookup, but neglects to verify whether the retrieved record belongs to the currently authenticated session. The builder tests it with their own account, confirms the invoice loads, and assumes security is handled. Incrementing the ID in the request downloads the billing records of every customer in the database.
  • Unbounded AI proxy routes. A backend endpoint that forwards user prompts straight to an upstream LLM API without authentication, rate limiting, or per-user token quotas. Within hours of deployment, automated scrapers discover the route and burn through thousands of dollars of API credits running their own workloads on the developer's card.

A language model gives you what you asked for. If you ask for a feature, you get the shortest path to making that feature run. Guard rails, perimeter isolation, and authorization boundaries are almost never part of the prompt.

From days to minutes: The LLM-accelerated adversary

Ten or twenty years ago, identifying and exploiting vulnerabilities required patient, manual craftsmanship.

When I conducted security audits or penetration tests, finding a route into a system was a matter of hours or days of deliberate, focused investigation. You ran nmap, collected port banners, ran fuzzers like ffuf or dirbuster against wordlists, and spent hours in the browser debugger deminifying JavaScript bundles. You combed through client-side bundles line by line looking for unadvertised endpoints, manually tested input parameters, and pieced together clues from an application stack trace to identify an unpatched ORM version.

It was skilled, cognitively exhausting detective work. That manual friction provided a measure of practical safety for unhardened applications.

Today, that friction has evaporated. The underlying workflow has not changed. The foundational tools are still the same: port scanners, fuzzers, HTTP clients, headless browsers, and network analyzers.

What has changed is that the entire pipeline is now driven by an LLM orchestration layer.

What once took hours or days of manual analysis now executes in minutes, at machine speed:

nmap / masscan  →  Real-time parsing of exposed ports and services
ffuf / katana   →  LLM maps every API endpoint from 5 MB of minified JS
HTTP 500 trace  →  Model instantly synthesizes a targeted exploit payload
Exploitation    →  Database dump and credential extraction in under 180s

The cognitive bottleneck has been removed. The machine does not get tired, it does not skim past a suspicious parameter name, and it ingests five megabytes of minified frontend code in seconds.

If an unhandled exception returns a Prisma or Drizzle stack trace, the LLM immediately recognizes the database schema structure, crafts an exploit payload specific to that exact library version, and fires it. If an endpoint responds with an atypical JSON error, the model adjusts its input structure dynamically and tries again.

The adversary is not relying on magic. They are running classic offensive automation, amplified by an analysis engine that reasons at wire speed.

The brutal asymmetry: Miss one detail and it is over

This brings us to the core engineering reality that vibecoding consistently obscures:

As a defender, you must get every single detail right. The attacker only needs you to miss one.

To keep an application secure on the public internet, you have to establish correctness across every single layer of the stack:

  • The network: strict binding to loopback interfaces or private WireGuard meshes, never exposing raw databases to 0.0.0.0, and understanding how container runtimes manipulate firewall rules.
  • The web server: denying access to dotfiles and configuration directories, enforcing security headers, locking down CORS policies, and terminating debug routes.
  • Authentication and session management: cryptographically sound session tokens, strict expiry, CSRF mitigations, and explicit authorization checks on every state-changing route.
  • The database layer: principle of least privilege, dedicated service roles without schema-altering permissions, and rigorously tested Row Level Security policies.
  • Secrets management: strict segregation between build-time client variables and server-side runtime credentials.

If your AI-generated app gets 99 out of 100 of these considerations right, but misses the single nginx directive that prevents downloading .env, or leaves one admin export endpoint unauthenticated—you are breached. The breach will not take weeks to happen; automated scanning engines will identify and exploit the gap within minutes of your DNS records propagating.

Public IPv4 space and certificate transparency logs are monitored around the clock. The internet does not wait for you to find product-market fit before evaluating your security posture.

Why AI will not fix this for you

The common retort is: "Can't I just tell the AI to make the app secure?"

No. Not in any meaningful sense.

A language model cannot reason about the physical and operational reality of your deployment. It does not know if your Linux host has AppArmor active, whether Docker has overridden your firewall, or if your upstream reverse proxy strips essential authorization headers. An LLM can generate code that looks like security boilerplate, but security is not syntax—security is the emergent behavior of the entire system under adversarial pressure.

Language models are trained on the public web, and the public web is overwhelmingly comprised of beginner tutorials, boilerplate templates, and quickstart documentation. In those guides, authentication, input sanitization, and network hardening are intentionally omitted to keep the code concise and readable. When an AI generates an application, that tutorial-grade compromise is its statistical baseline.

If you do not already possess the operational knowledge required to audit a production environment, you cannot instruct an AI to secure it for you. You will not even know which questions to ask.

The craft that remains

Generating syntax and assembling a responsive user interface has become remarkably easy. It is an extraordinary development for quick prototypes, local utilities, and exploratory builds.

Building is easy. Making software secure is something else entirely.

The moment an application is exposed to the public internet, it ceases to be a coding demo and becomes an active defense problem. The systems scanning your infrastructure do not sleep, do not lose focus, and analyze code at speeds no human can rival, driven by the very same models used to write your software.

What used to take hours or days now takes minutes.

If you do not understand how your operating system isolates processes, how your network handles packets, and how your endpoints authorize access at the machine level, you haven't deployed an application. You have deployed an open target.

Correspondence

Write to the lab

Commissions, collaborations, or a quiet hello. The person you write to is the person who does the work, and I reply within two working days.