Security checklist before you launch an app built with AI
What to check before opening your Lovable, Bolt or Cursor app to the public: exposed API keys, Supabase Row Level Security, auth, secrets, rate limits and backups.
Leer en español →
AI tools are great at making an app work. What they don't always do on their own is make it safe. They tend to optimize for a preview that looks right, not for keeping a stranger away from your users' data.
The good news: the security issues we see in vibe-coded apps are mostly the same handful, and you can check for them with a list. This is the one we use as a starting point. It isn't a replacement for a professional audit, but it will help you catch the serious problems before you share the link.
1. No secret keys in browser code
Anything that runs in the browser can be read by anyone. It doesn't matter that it's "tucked away" in some file: if it ships to the browser, it's public.
How to check:
- Open your published app, right-click → Inspect → Sources tab.
- Search across all files (
Ctrl + Shift + F, orCmd + Option + Fon a Mac) for strings likesk_live,sk-,service_role,secretorprivate. - Review your environment variables too. In Vite projects, anything prefixed
VITE_ends up in the browser; in Next.js, anything prefixedNEXT_PUBLIC_does.
What's fine and what isn't:
- The Supabase public key (anon or publishable) and the Firebase web config are designed to live in the browser. They are only safe if your database access rules are right (see step 2).
- The Supabase
service_role(or secret) key, Stripe secret keys, and keys for OpenAI or other AI providers must never be in the frontend. They belong on a server: an Edge Function, a serverless function, or your backend.
If you find an exposed key: deleting it from the code isn't enough, because it may still be in your Git history or older deploys. Rotate it: revoke it in the provider's dashboard, create a new one, and store it only on the server.
2. Row Level Security on every table
If you use Supabase, this is the big one. With the public key, anyone can query your database directly. The only thing deciding what each person can read or change is your Row Level Security (RLS) policies.
Check that:
- Every table holding user data has RLS enabled. The Supabase dashboard includes a Security Advisor that flags tables without RLS.
- Private tables don't have "anyone can read everything" policies (
using (true)). - Policies compare against the signed-in user, for example
auth.uid() = user_id. - Insert and update policies exist and check ownership too, not just select policies.
- Storage buckets with private files aren't marked public.
Hands-on test: create two user accounts. Signed in as user B, try to view or edit something that belongs to user A (for instance by changing an ID in the URL). If it works, you have a problem.
In Firebase, the equivalent is your Firestore and Storage Security Rules. If they're still in "test mode", anyone can read and write.
3. Auth that can't be bypassed
Hiding a button isn't protecting a feature. If the admin screen simply "doesn't show" for regular users but the database still accepts the request, anyone curious enough can use it.
Make sure that:
- Permissions (such as "is admin") are enforced in the database or on the server, not just in the UI.
- Users can't change their own role by editing their profile.
- Your server never trusts a
user_idsent from the browser; it should read it from the session. - Private pages don't flash data for a moment before redirecting to login.
- Your auth redirect URLs (in Supabase: Site URL and Redirect URLs) only point to domains you actually own.
4. Secrets and .env under control
- Your
.envfile should be in.gitignore. Confirm it was never pushed to your repository. - If the repo is or ever was public, check the history too: a secret in an old commit is still exposed.
- Keep production secrets in your hosting dashboard (Vercel, Netlify, Render…) or in your Edge Function secrets, not in the code.
- Use separate keys for testing and production.
5. Limits so nobody abuses your app
Without limits, someone can create thousands of accounts, send thousands of messages through your contact form, or burn through your AI credits in an afternoon.
- Add a CAPTCHA (such as Cloudflare Turnstile or hCaptcha) to sign-up, login and public forms. Supabase Auth supports this.
- If you have functions that call an AI API or send email, cap how many times each user can call them per minute.
- Set spending limits or alerts in your providers' dashboards where they're offered.
- Review the auth rate limits in your backend.
6. Validate everything that comes in
Never rely on form validation alone. It can be skipped by sending the request directly.
- Add constraints in the database: required fields (
not null), maximum lengths, allowed values. - Validate incoming data on the server inside your functions.
- For file uploads, restrict type and size.
- Don't render user content as raw HTML without sanitizing it (in React, be careful with
dangerouslySetInnerHTML). - Keep user-facing error messages free of internals like SQL queries or keys.
7. Backups you know how to restore
Security also means being able to recover. One prompt that drops a table, or one bad migration, can cost you all your data.
- Check which backups your database plan includes and how often they run.
- Make your own periodic copy as well (for example with
supabase db dumporpg_dump) and keep it outside the provider. - Remember that files in Storage aren't always included in a database backup.
- Restore at least once into a separate project. A backup you've never restored is an assumption.
8. Keep dependencies current
Run npm audit in your project to list packages with known vulnerabilities, and update the ones that have a fix. Take it slowly and test the app afterwards; upgrading everything at once can break things too.
The short version
- No secret keys in the browser; any exposed ones rotated.
- RLS enabled and tested with two accounts.
- Permissions enforced on the server, not only in the UI.
.envout of the repo, secrets stored in your host.- CAPTCHA and limits on anything that costs money or can be abused.
- Validation in the database and on the server.
- Your own backups, plus one tested restore.
npm auditreviewed.
Want a second pair of eyes?
If parts of this list made you uneasy, that's a good sign. Better that you find it than a stranger. At Devonauts we run security reviews for AI-built apps on Supabase, Firebase and other backends, and we explain every finding in plain language. See our AI app rescue or get in touch. If you're about to go live, read how to take your app from preview to production next.


