middletekmiddletek
Vibe CodingJan 15, 202631 min read

Vibe Coding Without Getting Hacked: A Practical Security Checklist

Vibe coding speeds up development but creates major security risks. Use this checklist to protect your AI-generated code from vulnerabilities.

Byron Jacobs
Byron Jacobs
Software Engineer
Vibe Coding Without Getting Hacked: A Practical Security Checklist

Vibe coding ships fast, and it ships risk

"Vibe coding" means letting an AI agent implement features from natural-language prompts with minimal human review. This approach compresses delivery time, but it also removes the safety checks people normally do without thinking.

That tradeoff is clear in the SUSVIBES benchmark. One of the best-performing agent and model setups produced about 61% functionally correct solutions. Yet, only about 10. 5% of those were secure. This means the vast majority of "working" code was still vulnerable.

Here is a practical, expanded checklist you can build into your development process and your AI agent rules.


The Security Checklist

1. Don't do sensitive "math" on the client

If business-critical logic runs on a user's device, like a browser or mobile app, you must assume it can be changed.

What does this even mean?

Imagine you're running an online store selling stickers. When someone buys a sticker for $5, their browser (the "client") sends a message to your server saying "I want to buy this." But what if a sneaky person opens their browser's developer tools and changes that message to say "I want to buy this for $0"? If your server just trusts whatever the browser says, congratulations, you just gave away free stickers!

The "client" is anything running on the user's device, their web browser, phone app, or desktop program. You have zero control over it. Users can inspect it, modify it, and lie to your server. So any important calculations, like "how much does this cost?" or "is this person allowed to do this?", need to happen on YOUR server, where nobody can tamper with them.


Move these to the server side:

  • Pricing, discounts, taxes, and credits
  • Scores, ranks, eligibility, and permissions
  • Anything that affects money, access, or trust

The client should only: collect input and display results. It should never be the source of truth.

Add these extra guardrails:

  • Treat every ID, role, flag, and price sent from the client as untrusted.
  • Always recompute totals and eligibility using authoritative data from your database and server rules, not from the data in the request itself.

🤖 Give this prompt to your agent to find and refactor:

"Review my codebase for any calculations involving pricing, discounts, permissions, scores, or eligibility that happen on the client side (in browser JavaScript, React components, or mobile app code). List each instance found. Then refactor each one so the calculation happens on the server instead, with the client only sending the raw inputs and displaying the server's response. Make sure the server re-validates all inputs and doesn't trust any calculated values from the client."


2. Validate and sanitize all inputs, including "internal" ones

Treat every input as hostile until proven otherwise. This includes forms, query parameters, headers, webhooks, file uploads, LLM outputs, and even data from services you think are trusted.

What does this even mean?

Think of your app like a nightclub with a bouncer. The bouncer's job is to check everyone at the door, not just strangers, but EVERYONE. Validation means checking that what someone gives you is actually what you expected. If you ask for an email address and someone types <script>alert('hacked!')</script>, that's obviously not an email!

"Sanitizing" means cleaning up the input, like removing dangerous characters or code that shouldn't be there. Even if data comes from somewhere you think is "safe" (like another part of your own system), you still need to check it. Why? Because attackers find sneaky ways to inject bad data through unexpected paths. It's like assuming food is safe just because it came from your kitchen, but what if someone snuck in and poisoned it?

The classic attack here is "SQL injection", where someone types database commands into a form field, and your app accidentally runs those commands. Imagine a login form where someone types '; DROP TABLE users; -- as their username. If you're not careful, you just deleted all your users!


Meet this minimum standard:

  • Use strict schemas for body, query, and header data, checking types, bounds, and allowed values.
  • Normalize and escape user text appropriately for storage versus display.
  • Only use parameterized queries, never build SQL with string concatenation.
  • Encode output in templates to prevent cross-site scripting (XSS).

Add these important upgrades:

  • Validate cross-field relationships, like ensuring a start date is before an end date, or that a total price matches the sum of line items when recalculated on the server.
  • Reject unknown fields by default. This helps stop attackers from smuggling in unexpected parameters.
  • Use separate schemas for creating versus updating records, as partial updates are a common source of vulnerabilities.

🤖 Give this prompt to your agent to audit and strengthen:

"Audit all API endpoints and form handlers in my codebase for input validation. For each endpoint: (1) Add or improve input validation using a schema library like Zod, Yup, or Joi, (2) Ensure all database queries use parameterized queries, find and fix any string concatenation in SQL, (3) Add output encoding to prevent XSS, (4) Reject unknown/unexpected fields in request bodies. Show me before and after for each file changed."


3. Put a speed limit on expensive actions

Any endpoint that triggers a cost or side effect is a potential attack surface. This includes authentication, sending emails or SMS, data exports, webhooks, AI calls, and file uploads.

What does this even mean?

Imagine your app has a "forgot password" feature that sends emails. What if someone writes a script that hits that button 10,000 times per second? You'd burn through your email service budget in minutes (that's called a "denial of wallet" attack), and you might get blocked by email providers for spamming.

"Rate limiting" is like a speed limit for your app. It says "hey, you can only do this action X times per minute." This protects you from attackers who try to brute-force passwords (trying millions of password combinations), spam your services, or rack up your cloud bills.

Think of it like a pizza shop that says "max 3 slices per person." Without that rule, one hungry person could eat everything and leave nothing for other customers.


Do:

  • Rate limit per IP and per user, and sometimes per organization or tenant.
  • Set quotas for costly endpoints with daily or monthly budgets.
  • Implement backoff or cooldown periods for repeated failures.
  • Use CAPTCHA or attestation only where it makes sense, like in high-abuse, low-friction areas.

Important detail: also rate-limit verification attempts, including OTP codes, reset tokens, and invite codes.


🤖 Give this prompt to your agent to implement:

"Add rate limiting to my application. Identify all endpoints that: send emails/SMS, handle authentication (login, password reset, OTP verification), make AI/LLM calls, process payments, handle file uploads, or trigger webhooks. Implement rate limiting using an appropriate library for my stack (e.g., express-rate-limit, bottleneck, or similar). Set sensible defaults: 5 attempts per minute for auth endpoints, 10 per minute for email/SMS, 100 per hour for AI calls. Add both per-IP and per-user limits where users are authenticated."


4. Don't log sensitive data (and don't "debug print" in production)

AI-generated fixes often suggest logging everything. That approach leaks secrets into logs, APM tools, error trackers, browser consoles, and support tickets.

What does this even mean?

When you're building an app, you often add console.log() statements to see what's happening, like "user logged in" or "payment processed." But here's the problem: those logs get stored somewhere, and lots of people might be able to read them, your teammates, your error tracking service, maybe even hackers if there's a breach.

Now imagine you accidentally log someone's password: console.log("User password:", password). That password is now sitting in a log file somewhere, maybe forever. Same goes for credit card numbers, API keys, or someone's home address.

It's like writing your PIN number on a sticky note and leaving it on your desk. Sure, it's convenient when you need it, but anyone walking by can see it!


Never log:

  • Passwords, reset tokens, or API keys
  • Authentication headers or session cookies
  • Full payment details
  • Raw personally identifiable information, especially full addresses or IDs

Prefer:

  • Redaction helpers with allow-lists
  • Structured logs with correlation or request IDs
  • Separate "audit logs" (who did what) from "debug logs" (why it broke)

🤖 Give this prompt to your agent to scan and clean up:

"Audit my codebase for unsafe logging practices. Find all console.log, logger.info, logger.debug, and similar logging statements. Flag any that might contain: passwords, tokens, API keys, authorization headers, cookies, credit card numbers, or personal information like emails, addresses, or phone numbers. Create a logging utility/wrapper that automatically redacts sensitive fields. Replace direct logging calls with this safe wrapper. Remove any debug logging that shouldn't be in production code."


5. Audit with a "rival" (second pass, different tool or model) and automated scanners

A second opinion catches patterns the first agent misses.

What does this even mean?

You know how teachers say to proofread your essay, then have a friend read it too? They'll catch mistakes you missed because you're too close to your own work. The same thing happens with AI-generated code!

If you used Claude to write your code, have GPT-4 review it (or vice versa). Different AI models have different blind spots, what one misses, another might catch. It's like getting a second opinion from a different doctor.

Automated scanners are like spell-checkers, but for security. They automatically look for common mistakes like "you forgot to check if the user is logged in" or "this package you're using has a known security hole." These tools aren't perfect, but they catch the obvious stuff so you can focus on the tricky problems.


Do all three before merging code:

  • SAST and lint rules, like Semgrep plus ESLint security rules
  • Dependency scanning with npm audit, Snyk, or Dependabot
  • A second audit prompt using a different model (see the rules section below)

Also add:

  • A PR checklist that forces reviewers to answer: "Where is the authentication? Where is the validation? Where are the rate limits? Where is the logging redaction?"

🤖 Give this prompt to your agent to set up:

"Set up automated security scanning for my project. (1) Add ESLint with security-focused plugins (eslint-plugin-security, eslint-plugin-no-secrets). (2) Configure Semgrep with default security rules. (3) Set up npm audit or Snyk to run on every pull request. (4) Create a PR template with a security checklist that asks: Where is authentication checked? Where is input validated? Are there rate limits? Is logging safe? (5) Show me how to run these checks locally before pushing code."


6. Keep dependencies up to date (this is not optional)

Outdated packages are one of the easiest ways to get compromised.

What does this even mean?

Your app probably uses hundreds of packages that other people wrote, things like React, Express, or that library for handling dates. These packages sometimes have security bugs. When someone discovers a bug, the package maintainers fix it and release a new version.

But here's the thing: if you don't update to that new version, you're still running the buggy code! Attackers literally search for apps using old, vulnerable versions of popular packages. It's like knowing your front door lock has a flaw that burglars know about, but not bothering to replace it.

The Log4j vulnerability in 2021 affected millions of applications because everyone was using an old version of a logging library. Companies that updated quickly were fine; companies that didn't got hacked.


Process improvements:

  • A weekly update cadence with automated PRs
  • CI that runs on upgrade PRs for tests and builds
  • A fast patch path for critical CVEs
  • Lockfile discipline to avoid "floating" versions in production

🤖 Give this prompt to your agent to check and configure:

"Help me set up automated dependency management. (1) Run npm audit (or equivalent for my package manager) and show me all current vulnerabilities. (2) Set up Dependabot or Renovate to automatically create PRs for dependency updates. (3) Configure it to group minor updates weekly and create immediate PRs for security patches. (4) Ensure my CI pipeline runs tests on all dependency update PRs. (5) Fix any high or critical vulnerabilities found right now."


7. Handle errors without revealing internals

Verbose errors help attackers map your system.

What does this even mean?

When something breaks in your app, the error message contains clues about how your app works, what database you're using, what file paths exist, what libraries you have. For a regular user, this is just confusing gibberish. But for a hacker, it's a treasure map!

Imagine your app crashes and shows: Error: Connection failed to PostgreSQL at 192.168.1.50:5432, user 'admin'. You just told the attacker your database type, internal IP address, port, and username. That's like leaving your house keys under the doormat AND putting a sign that says "keys under mat."

The fix is simple: show users a friendly "Oops, something went wrong!" message, while logging the real error details somewhere only your team can see.


Public errors:

  • Use a generic message like "Something went wrong"
  • Do not include stack traces, SQL details, file paths, or internal hostnames

Private logs:

  • Include full stack traces
  • Include request IDs and minimal necessary context, properly redacted

Important add-on: ensure error responses are consistent to avoid account enumeration via different messages or status codes.


🤖 Give this prompt to your agent to review and fix:

"Review my error handling throughout the codebase. (1) Find all places where errors are returned to users and ensure they show generic, user-friendly messages without stack traces, file paths, database details, or internal IPs. (2) Create a centralized error handler that logs full error details privately (with a correlation ID) while returning safe messages publicly. (3) Check that error responses are consistent, for example, 'invalid username' and 'invalid password' should both just say 'invalid credentials' to prevent account enumeration. (4) Make sure production builds have debug mode disabled."


8. Authentication: secure sessions and tokens

"Working auth" is not automatically safe auth.

What does this even mean?

Authentication is how your app knows WHO someone is, it's the login system. But just because login "works" doesn't mean it's secure!

When you log in to a website, it gives you a "session token", like a wristband at a concert that proves you paid for entry. The problem is WHERE you store that wristband. If you store it somewhere anyone can grab it (like in localStorage, which JavaScript can read), a hacker could steal it using XSS attacks and pretend to be you.

"HttpOnly cookies" are like a special wristband pocket that JavaScript can't reach into, only the browser and server can access it. That's much safer!

Also, passwords need to be stored in a special scrambled format called "hashing." If someone steals your database, they shouldn't see actual passwords, just scrambled text that's nearly impossible to unscramble. Using weak hashing is like locking your door but leaving a copy of the key taped to the window.


Very important checks:

  • Use HttpOnly cookies for session tokens. Avoid storing JWTs in localStorage.
  • Set cookie flags: Secure, HttpOnly, and a SameSite policy appropriate for your application flow.
  • Use short-lived access tokens and implement rotation for refresh tokens if you use them.
  • Require multi-factor authentication for admin and other high-privilege accounts.
  • Store passwords with a strong algorithm like Argon2, bcrypt, or scrypt. Never store them in plain text or with fast hashes.

🤖 Give this prompt to your agent to audit and secure:

"Audit and secure my authentication system. (1) Check where session tokens/JWTs are stored, if using localStorage, migrate to HttpOnly cookies. (2) Ensure all cookies have Secure, HttpOnly, and appropriate SameSite flags set. (3) Verify passwords are hashed with Argon2, bcrypt, or scrypt, not MD5, SHA1, or plain text. (4) Check that access tokens expire within a reasonable time (e.g., 15 minutes to 1 hour). (5) If using refresh tokens, ensure they're rotated on each use. (6) Add or verify MFA requirement for admin accounts."


9. Authorization: Prevent IDOR and Broken Access Control

Most real-world application breaches happen because someone can access another user's data.

What does this even mean?

Authentication asks "who are you?" Authorization asks "are you allowed to do this?" They're different!

IDOR (Insecure Direct Object Reference) is when you can access someone else's stuff just by changing a number in the URL. Imagine you're viewing your order at mystore.com/orders/12345. What happens if you change that to orders/12346? If you can suddenly see someone ELSE's order... that's IDOR!

It's like an apartment building where every door has a number, but none of the doors have locks. Sure, you KNOW your apartment is #5, but nothing STOPS you from walking into #6.

The fix is always checking "does this user actually have permission to access this specific thing?" before showing it to them.


Rules to prevent this:

  • Filter every data read and write operation by the authenticated user, organization, or tenant.
  • Centralize authorization checks in one place. Don't scatter conditional checks throughout your code.
  • Add tests that specifically attempt cross-tenant access to verify object-level authorization.

A warning sign: Any endpoint that accepts parameters like userId, orgId, role, or isAdmin directly from the client.


🤖 Give this prompt to your agent to investigate and patch:

"Audit my codebase for IDOR and broken access control vulnerabilities. (1) Find all database queries that fetch records by ID from user input (URL params, query strings, request body). (2) For each one, verify there's a check that the authenticated user owns or has permission to access that record. (3) Find any endpoints that accept userId, orgId, role, or isAdmin from the client and flag them. (4) Create or improve a centralized authorization middleware/helper. (5) Add test cases that specifically attempt to access another user's resources, these tests should FAIL (be denied)."


10. CSRF and Cross-Origin Safety

If you use cookies for authentication, you must protect state-changing requests.

What does this even mean?

CSRF (Cross-Site Request Forgery) is a trick where a malicious website makes YOUR browser do something on a site where you're logged in, without you knowing!

Here's how it works: You're logged into your bank (you have that session cookie, remember? ). Then you visit a sketchy website. That website has hidden code that tells your browser "hey, go to the bank website and transfer $1000 to this account." Because you're already logged in, the bank thinks it's really you!

It's like if someone could forge your signature just by getting you to hold a special pen, you didn't mean to sign anything, but the signature is real.

CORS (Cross-Origin Resource Sharing) is a related concept, it controls which websites can talk to your server. Setting it to "everyone can access everything" is like removing all the locks from your house.


What to do:

  • Use CSRF tokens or rely on SameSite cookie protections where appropriate.
  • Confirm the Origin or Referer headers for sensitive actions.
  • Avoid CORS misconfigurations. Never use Access-Control-Allow-Origin: * when credentials are involved.

🤖 Give this prompt to your agent to verify and protect:

"Secure my application against CSRF and CORS attacks. (1) If using cookie-based auth, implement CSRF protection using tokens or verify SameSite cookie settings are properly configured. (2) Audit my CORS configuration, find any instances of Access-Control-Allow-Origin: '*' with credentials and fix them. (3) For sensitive actions (password change, email change, payments, account deletion), add Origin/Referer header verification. (4) Create a test that attempts a CSRF attack to verify protection works."


11. XSS Hardening Beyond Basic Output Escaping

Modern cross-site scripting often comes from rich content like markdown, WYSIWYG editors, templates, HTML emails, SVGs, or components meant for safe previews.

What does this even mean?

XSS (Cross-Site Scripting) is when an attacker sneaks their code into YOUR website, and then it runs in other users' browsers. The classic example: a comment box where someone types <script>stealCookies()</script>. If you display that comment without cleaning it up, everyone who views it runs the attacker's code!

Modern XSS is sneakier. It hides in markdown editors, rich text, uploaded SVG images, or anywhere you let users add "formatted" content. Even a simple link could be malicious: <a href="javascript:badStuff()">Click me! </a>

The fix is treating all user content like it's potentially dangerous, always "sanitize" it (strip out the dangerous parts) before displaying it. It's like running a letter through a scanner before opening it to check for anthrax.


Key controls to implement:

  • Avoid injecting raw HTML unless you sanitize it first with a vetted, secure library.
  • Sanitize markdown and HTML, strictly locking down which tags and attributes are allowed.
  • Add a Content Security Policy where it's feasible for your application.
  • Never reflect untrusted user input directly into script contexts, URLs, or inline event handlers.

🤖 Give this prompt to your agent to harden:

"Harden my application against XSS attacks. (1) Find all uses of dangerouslySetInnerHTML (React), v-html (Vue), innerHTML, or equivalent unsafe HTML insertion. (2) For each one, either remove it or add sanitization using DOMPurify or a similar trusted library. (3) Review any markdown rendering to ensure it's sanitized. (4) Check for user input reflected in href, onclick, or other dangerous attributes. (5) Add a Content-Security-Policy header that blocks inline scripts where possible. (6) Create tests that attempt common XSS payloads to verify protection."


12. SSRF and "Fetch a URL" Features

Any feature where your server fetches a URL, like for link previews, image imports, webhook testing, or scraping, is a classic entry point for server-side request forgery.

What does this even mean?

SSRF (Server-Side Request Forgery) happens when your server fetches a URL that a user provides, and an attacker tricks it into fetching something it shouldn't.

Say you have a "preview this link" feature. User gives you a URL, your server fetches it and shows a preview. Sounds harmless, right? But what if someone enters http://localhost:8080/admin/delete-everything or http://169.254.169.254/metadata (a special address that exposes cloud secrets)?

Your server is inside your network, it can access internal services that the public internet can't reach. By tricking it into making requests, attackers can reach things they shouldn't.

It's like asking a hotel employee to deliver a package, if they don't check what's inside, you could trick them into delivering something to restricted areas.


Controls to put in place:

  • Prefer an allow-list of approved domains.
  • Block requests to private IP ranges and internal metadata endpoints.
  • Enforce strict timeouts, size limits, and redirect limits on all fetches.
  • Do not pass arbitrary headers from user input through to the fetched URL.

🤖 Give this prompt to your agent to locate and lock down:

"Audit my application for SSRF vulnerabilities. (1) Find all places where the server fetches URLs based on user input (image imports, link previews, webhooks, URL screenshots, etc.). (2) For each one, add validation that blocks: private IP ranges (10.x, 172.16-31.x, 192.168.x, 127.x), localhost, and cloud metadata IPs (169.254.169.254). (3) Implement an allow-list of approved domains where possible. (4) Add timeouts, size limits, and redirect limits to all fetch operations. (5) Ensure user-controlled data isn't passed as headers to fetched URLs."


13. File Uploads: Treat Them Like Malware and Bombs

File uploads can be used for cross-site scripting, remote code execution, storage abuse, and denial-of-wallet attacks.

What does this even mean?

File uploads are one of the scariest features to implement because attackers LOVE them. They can upload:

  • Malware disguised as images
  • Files so large they fill up your storage (denial-of-wallet)
  • HTML or SVG files that contain hidden scripts (XSS)
  • Files with special names like ../../../etc/passwd that try to escape their folder

Never trust the file extension! Someone can rename virus.exe to cute-cat.jpg. You need to actually check what the file IS, not just what it claims to be.

It's like accepting packages at a front desk, you need to X-ray them, check who sent them, limit their size, and definitely not let the delivery person choose where to store them.


The minimum bar for security:

  • Size limits, content-type validation, and magic-byte sniffing
  • Random file names (never trust provided names or paths)
  • Store files outside the web root or behind signed URLs
  • Process images server-side to strip metadata
  • Scan for malware if the risk profile justifies it

🤖 Give this prompt to your agent to fortify:

"Secure all file upload functionality in my application. (1) Find all file upload handlers. (2) For each one: add file size limits (e.g., 10MB max), validate content-type using magic bytes (not just the extension), generate random filenames (never use user-provided names), and validate the file extension against an allow-list. (3) Ensure uploaded files are stored outside the web root or served through signed URLs with proper Content-Disposition headers. (4) If handling images, add processing to strip EXIF metadata. (5) Add path traversal protection, reject any filenames containing '../' or absolute paths."


14. Webhooks: verify signatures and make them idempotent

Webhooks are unauthenticated by default unless you add authentication.

What does this even mean?

Webhooks are when another service "calls" your app to notify you something happened, like "payment successful!" from Stripe or "new commit!" from GitHub. The problem? Anyone can send a request to your webhook URL pretending to be Stripe!

Without verification, an attacker could send fake "payment successful" webhooks and get free stuff. Signature verification is like checking the wax seal on a letter, only the real sender has the right stamp.

"Idempotent" means "doing it twice has the same effect as doing it once." If Stripe accidentally sends the same webhook twice, you shouldn't charge the customer twice or ship two orders. You need to remember "I already handled this one" and skip duplicates.


Do:

  • Verify signatures using HMAC or public keys, depending on the provider
  • Implement replay protection with timestamps or nonces
  • Use idempotency keys to avoid double-charging or double-fulfilling
  • Separate accepting a webhook from processing it by queuing and validating first

🤖 Give this prompt to your agent to validate and safeguard:

"Secure all webhook endpoints in my application. (1) Find all webhook handlers. (2) For each one, implement signature verification using the provider's recommended method (HMAC, public key, etc.). (3) Add replay protection by checking timestamps, reject webhooks older than 5 minutes. (4) Implement idempotency by storing processed webhook IDs and skipping duplicates. (5) Separate webhook receipt from processing, accept and queue webhooks quickly, then process them asynchronously with proper error handling. (6) Add tests that attempt to send forged webhooks to verify they're rejected."


15. Secrets management: stop leaks before they happen

Never commit secrets to version control; scan for them with tools like gitleaks.

What does this even mean?

"Secrets" are things like API keys, database passwords, and encryption keys, the private information your app needs to connect to other services. The problem is these secrets need to live SOMEWHERE, and developers often put them in the wrong places.

The worst place? Committed to Git. Once a secret is in your Git history, it's there FOREVER (even if you delete the file later). Bots constantly scan GitHub for accidentally committed AWS keys and use them within minutes.

Think of secrets like your house keys. You wouldn't post a photo of them on Instagram (someone could copy them), leave them lying around the office (coworkers might "borrow" them), or use the same key for your house, car, and office (if one is stolen, everything is compromised).


Very important controls:

  • Never commit secrets to version control; scan for them with tools like gitleaks
  • Use separate credentials for development, testing, and production
  • Rotate keys regularly and immediately upon any exposure
  • Apply the principle of least privilege for service accounts with scoped tokens

🤖 Give this prompt to your agent to scan and secure:

"Audit my project for secrets management issues. (1) Scan the entire Git history for accidentally committed secrets using gitleaks or trufflehog, check for API keys, passwords, tokens, and private keys. (2) List any secrets found and their commit locations. (3) Check that . env files are in . gitignore. (4) Verify that different credentials are used for development vs. production. (5) Set up a pre-commit hook to prevent future secret commits. (6) If any secrets were found in Git history, provide instructions for rotating them and cleaning the Git history."


16. Secure defaults for infrastructure and data

Many application vulnerabilities stem from configuration and permissions issues.

What does this even mean?

Your code might be secure, but what about everything around it? Your database, your file storage, your network settings, these are all part of your app's security.

The classic mistake: creating an Amazon S3 bucket to store user files and leaving it "public" because that's easier. Suddenly anyone on the internet can download all your users' private files!

"Least privilege" means giving each part of your system only the permissions it absolutely needs. Your web server probably doesn't need permission to delete your entire database, so don't give it that permission! That way, even if hackers break in through the web server, the damage is limited.

It's like giving the babysitter a key to the house but not the code to your safe.


Baseline:

  • Set storage buckets and services to private by default
  • Configure database roles with the least privilege necessary
  • Control network egress in sensitive environments
  • Maintain backups and conduct regular restore drills to test them

🤖 Give this prompt to your agent to review and configure:

"Review my infrastructure and data security configuration. (1) Check all cloud storage buckets (S3, GCS, Azure Blob), ensure they're private by default and list any public buckets. (2) Review database user permissions, verify the application uses a role with minimum necessary privileges (not root/admin). (3) Check for any services exposed to the public internet that should be internal-only. (4) Verify backups are configured and test that a restore would work. (5) List any overly permissive IAM policies or service account permissions that should be scoped down."


17. Observability that helps defense, not attackers

Keep audit logs for sensitive actions like role changes, billing events, data exports, and admin access.

What does this even mean?

"Observability" means being able to see what's happening in your app, through logs, metrics, and alerts. Good observability helps you catch attacks in progress and investigate them afterward.

But there's a balance! Log too little, and you won't know when you're being attacked. Log too much (especially sensitive data), and your logs become a target themselves.

The goal is to log WHAT happened (user X changed their password, admin Y exported data) without logging sensitive details (the actual password, the exported data contents). It's like a security camera that records who enters a building, but doesn't record their PIN codes.

Alerts are your early warning system. If someone tries 1000 different passwords in a minute, you should know about it immediately, not discover it three months later.


Do:

  • Keep audit logs for sensitive actions like role changes, billing events, data exports, and admin access
  • Set up alerts for anomalies such as login spikes, export spikes, or bursts of failed authentication
  • Build security-relevant dashboards to monitor rate-limit rejections, webhook failures, and permission denials

🤖 Give this prompt to your agent to enhance:

"Improve security observability in my application. (1) Identify all sensitive actions (login, password change, role changes, data exports, billing changes, admin actions) and ensure each one creates an audit log entry with: who, what, when, and from where (IP). (2) Set up alerts for: more than 10 failed logins per minute, unusual data export volumes, admin actions outside business hours, and rate limit violations. (3) Create a security dashboard showing: failed auth attempts over time, rate limit hits, permission denied errors, and webhook failures. (4) Verify audit logs don't contain sensitive data like passwords or tokens."


18. A "security gate" for AI-generated diffs

Before merging code, require the diff to answer specific security questions.

What does this even mean?

A "diff" is the list of changes between your old code and your new code, what you added, removed, or modified. When AI writes code for you, you need to review those changes before merging them into your main codebase.

Think of this as a security checkpoint at an airport. Every piece of AI-generated code needs to go through the scanner before it's allowed in. The scanner asks specific questions: "Where does this code check permissions? Where does it validate input? Could this leak secrets?"

This checklist forces you (or another AI) to consciously verify that security wasn't forgotten in the rush to ship features. Without it, vulnerable code slips through because "it works" and nobody thought to ask "but is it safe?"


The diff must answer:

  • What assets are involved, such as money, PII, or access?
  • What are the entry points, like API routes, webhooks, or uploads?
  • Where are the trust boundaries, for example between client and server or server and third-party?
  • Where are the authentication and authorization checks?
  • Where are the schemas and validation?
  • Where are the rate limits and quotas?
  • What steps were taken to prevent IDOR, injection, XSS, SSRF, and secrets leakage?

🤖 Give this prompt to your agent to analyze before every merge:

"Perform a security review of this code change. Answer each question: (1) What sensitive assets does this code touch (money, user data, permissions, secrets)? (2) What are the entry points (API routes, webhooks, uploads, scheduled jobs)? (3) Where are the trust boundaries (client/server, your code/third-party)? (4) Where and how is the user authenticated? (5) Where and how is authorization checked, can users only access their own data? (6) What input validation exists? Are schemas enforced? Are unknown fields rejected? (7) Are there rate limits on expensive operations? (8) Could this code be vulnerable to: IDOR, SQL injection, XSS, SSRF, or CSRF? (9) Are any secrets or sensitive data logged or exposed in errors? List any concerns and suggest specific fixes."


Updated . cursorrules (expanded)

# Security rules for AI-generated code (Cursor / agent rules)

Never trust client-side values for sensitive information like money, permissions, scoring, eligibility, roles, or IDs. Always recompute these values on the server and enforce authorization server-side.

## Endpoint Requirements

Every new or changed endpoint must include these six checks:

- Authentication to confirm who is making the request. 
- Authorization to verify they are allowed to perform the action.
- Validation to ensure all input is safe.
- Rate limiting or quotas to prevent abuse.
- Redacted logging for safe debugging. 
- Tests that prevent cross-user or cross-tenant access.

## Input Validation

Validate and sanitize all inputs, including request bodies, query parameters, headers, webhooks, file uploads, and outputs from LLMs.  Enforce strict schemas, bounds, and allow-lists.  Reject unknown fields by default.  Only use parameterized queries for database interactions.

## Authorization and IDOR Prevention

Authorization must prevent Insecure Direct Object References (IDOR). Every read or write operation must be scoped to the authenticated user, organization, or tenant. Never accept client-provided values like orgId, userId, isAdmin, or role as proof of authority.

## Rate Limiting and Quotas

Add rate limiting and quotas to any endpoint that triggers a cost or side effect. This includes authentication, email or SMS sending, AI generation, uploads, exports, webhooks, and billing actions. Implement per-IP and per-user limits, and include backoff or cooldown periods for repeated failures.

## Secure Logging

Do not log secrets or sensitive user data. Redact tokens, passwords, headers, cookies, payment details, and personally identifiable information by default.  Avoid adding "log the full request" to production code. 

## Error Handling

Implement secure error handling.  Show generic messages to users while logging detailed information privately with correlation or request IDs, ensuring logs are redacted.  Avoid account enumeration by using consistent error messages and status codes.

## Cross-Site Scripting (XSS) Safety

Never render unsanitized HTML or markdown.  Avoid using dangerouslySetInnerHTML unless the content is properly sanitized. Prefer framework-safe templating and output encoding.

## CSRF and CORS Safety

Do not use wildcard CORS configurations with credentials. Protect all state-changing requests that use cookie-based authentication. 

## Server-Side Request Forgery (SSRF) Safety

Do not fetch arbitrary URLs provided by users without using allow-lists and blocking access to private networks. 

## Dependency Management

Keep all packages updated.  Enable dependency scanning in your continuous integration pipeline, and patch critical Common Vulnerabilities and Exposures urgently.

## Pre-Merge Security Review

Before merging any code, run a security review that includes: 

- A static scan using linting or SAST tools. 
- A dependency scan. 
- A second-model audit prompt:  "Audit this code change for authorization issues, broken access control or IDOR, injection vulnerabilities, XSS, SSRF, CSRF, secrets leakage, unsafe deserialization, and gaps in rate limiting or quotas."
Tags
SecurityAI CodingVibe CodingCybersecurityBest PracticesDevelopment