Security
The security basics AI-built apps skip — secrets, logins, row-level security, bot abuse — and the isolation that keeps untrusted code contained.
51 posts · page 1 of 2
What Is XSS? Cross-Site Scripting Explained for Beginners
Cross-site scripting lets an attacker run their JavaScript in your users' browsers — stealing sessions, changing pages, acting as the user. How XSS works, the three types, why React mostly protects you, the escape hatches that don't, and the defences that matter.
Where Should User Uploads Go? Object Storage Explained
Profile photos that vanish after a deploy, a database bloated with images, a public bucket full of private documents. Where uploaded files should live, how object storage works, and the three decisions — public or private, who uploads, and how files are served — that keep uploads fast and safe.
What Is Rate Limiting? Protecting Your App From Too Many Requests
Rate limiting caps how many requests someone can make in a period. Why every public app needs it — for login forms, sign-ups, AI features, and APIs — how it works, what a 429 response means, where to add limits, and what to do when you hit someone else's.
What Is HTTPS? The Padlock, Explained for Beginners
HTTPS is why the browser shows a padlock instead of 'Not secure'. What it protects, what it doesn't, what a certificate is, why your app needs it before launch, and how to fix the common 'mixed content' and certificate errors.
What Is a JWT? JSON Web Tokens Explained Simply
Supabase, Firebase, Auth0, and Clerk all hand your app JWTs. What a JSON Web Token is, its three parts, why anyone can read it but nobody can forge it, how apps use it for login, and the mistakes AI-generated code makes with tokens.
A Security Checklist for Vibe-Coded Apps
AI-built apps fail security in predictable ways: open databases, keys in the browser, authorization checked only in the UI. A practical checklist for non-security people — what to check, how to test it yourself, and what to fix before real users arrive.
Validating Input With Zod: One Schema for Forms, APIs, and Types
Every trust boundary — request bodies, query strings, webhooks, environment variables, AI output — needs runtime validation TypeScript can't provide. Using Zod schemas at each boundary, sharing them between client and server, stripping unknown keys, and useful errors.
How to Update Your App's Dependencies Safely
The packages your app is built on get security fixes and new versions constantly. Ignore them and you accumulate risk; update carelessly and the app breaks. What version numbers mean, a safe routine for updating, how to handle security warnings, and how to let an AI do the tedious part.
Supabase Row-Level Security Explained (for People Who Didn't Write the Policies)
If your app talks to Supabase from the browser, row-level security is the only thing standing between your users' data and anyone who opens the developer tools. What RLS is, how to read the policies your AI tool wrote, the four mistakes that leave data exposed, and how to test it yourself.
How to Stop Bots From Running Up Your AI App's Bill
If your app calls an AI model on a user's behalf, every request costs you money — and a bot, a scraper, or one determined user can make thousands of them overnight. Rate limits, usage caps, provider spending limits, and the architecture that keeps a surprise bill from happening.
How to Stop an AI Agent From Deleting Your Production Database
In July 2025 an AI coding agent deleted a company's production database during a code freeze. It wasn't a freak event — it was the predictable result of giving an agent production credentials. Six controls that make it structurally impossible, not just unlikely.
SSH Keys Explained: Set Them Up Once, Properly
SSH keys are how you log into servers and push to GitHub without passwords. What the two halves of a key pair do, how to create a modern one, using ssh-agent and a config file so you stop retyping things, and the habits that matter: passphrases, one key per device, never sharing a private key.
SQL Injection Explained: The Classic Attack and the One-Line Fix
SQL injection lets an attacker rewrite your database queries by typing into a form. How it works with a simple example, what damage it can do, why parameterized queries and ORMs prevent it, the places AI-generated code still gets it wrong, and how to check your app.
Soft Deletes and Audit Logs: Keeping History Without Making a Mess
Deleting rows is irreversible; hiding them has costs too. When to use soft deletes, how to implement them without leaking 'deleted' data (partial indexes, unique constraints, views, RLS), the privacy tension with erasure requests, and how to build an audit log with triggers or application events.
"Sign in with Google" Explained: OAuth for Beginners
Social login lets users skip creating a password. How 'Sign in with Google' (and GitHub, Apple, Microsoft) actually works, what OAuth and OpenID Connect are, what redirect URIs and client secrets are, and why it breaks when you move from localhost to your real domain.
Securing MCP Servers: Threats and Controls for Tool-Connected Agents
An MCP server turns a model's text into real actions against real systems. The threat model — tool poisoning, prompt injection via tool output, confused deputies, token passthrough, DNS rebinding on local servers, over-broad scopes — and the controls for building and deploying MCP servers safely.
Secrets Management Beyond .env Files
.env files are fine on a laptop and fragile everywhere else. Where secrets should live in production and CI, secret managers vs platform env vars, OIDC to remove long-lived CI credentials, rotation, least privilege, keeping secrets out of logs and AI agent context, and a practical maturity path.
How to Run AI-Generated Code Safely
AI-generated code is usually well-intentioned and occasionally destructive, and the packages it installs are a supply-chain risk of their own. A practical, layered approach — what the code can see, reach, consume, and outlive — with a hardened Docker command you can use today.
Rootless Containers and User Namespaces: What They Actually Protect
Root in a container is root on the host unless something remaps it. How user namespaces work, subuid/subgid ranges, Docker userns-remap vs rootless mode vs Podman, Kubernetes hostUsers: false, the file-ownership and networking costs, and where rootless fits.
Designing a REST API That Won't Embarrass You Later
APIs are hard to change once clients depend on them. The conventions that keep a REST API predictable — resource naming, methods, status codes, errors, pagination, validation, idempotency, and versioning — with the specific mistakes AI-generated APIs tend to make.
Prompt Injection in Coding Agents: A Threat Model
A coding agent with a shell, credentials, and network access reads text written by strangers all day. A threat model — sources, capabilities, sinks — why detection-based defences fail, and the architectural controls that actually bound the damage.
Direct-to-Storage Uploads With Presigned URLs
Proxying uploads through your server wastes memory, bandwidth, and request time. How presigned URLs let browsers upload straight to S3, R2, or GCS: PUT vs POST policies, enforcing size and type, bucket CORS, confirming uploads, multipart, and serving private files.
Password Hashing Explained: Why You Never Store Passwords
A well-built app doesn't know your password — it stores a hash. What hashing is, why fast hashes like MD5 and SHA-256 are wrong for passwords, what salts do, why bcrypt and Argon2 exist, and how to check that your AI-built app got it right.
npm Supply Chain Security: Install Scripts, Release Cooldowns, Provenance, and Trusted Publishing
Compromised maintainer accounts and self-propagating worms made npm installs an attack surface. The threat model, disabling install scripts, release cooldowns in npm, pnpm, Yarn, and Bun, lockfile discipline, provenance, trusted publishing, and isolating installs.