FitbuildBuild apps with AI, no coding experience needed
Menu

Security checklist for AI-built apps

An app built with AI is not safe because the preview loads. Before you share the link or take money, check seven things. Paste one prompt at a time. Tell the tool to report first. Do not let it "fix" production while it still has the production password. The docs behind this list were checked 2026-10-22.

Do these before anyone else opens the app

  1. 1. No secret key in a frontend env var.
  2. 2. Supabase row level security on every exposed table, with a policy, not only a switch.
  3. 3. Firebase rules out of Test mode, and tighter than "any logged-in user."
  4. 4. The server checks who is signed in and which rows they own.
  5. 5. The Stripe webhook checks the signature before it marks an order paid.
  6. 6. A backup exists somewhere the agent cannot delete in one command.
  7. 7. The agent's credentials point at a development database, not production.

Where the app is deployed is a separate choice: where to host. Which database you picked is Supabase vs Firebase. Checkout comes after this list: payments.

1. Secret keys stay off the browser

Vite exposes every VITE_ variable in the client bundle. Next.js does the same for NEXT_PUBLIC_. A name that looks private is not private if the prefix puts it in the JavaScript your visitors download. Stripe says only the publishable key (pk_) is safe in frontend code. Secret keys (sk_) and restricted keys are not. Supabase's row level security guide says the service role bypasses those rules, so it stays on the server. A publishable or anon key in the browser is expected, and it is only safe when the rules below are on.

Look through this project and report. Do not change files yet.

1. List every environment variable whose name starts with VITE_ or NEXT_PUBLIC_.
2. For each one, say whether the value is a secret or a value the provider says can live in the browser. Secrets include database passwords, a Supabase service-role or secret key, a Stripe secret or restricted key, and a Stripe webhook signing secret. Values that can be public, once the other controls on this checklist are on, include a Supabase publishable or anon key, a Stripe publishable key, and the Firebase web config.
3. Name the file where each secret is read. Do not print the secret values.
4. Do not rotate, delete, or deploy anything. Stop after the report.

2. Turn on Supabase row level security

Supabase's docs say a table in an exposed schema without row level security is readable and writable by any role that has a grant. Enable it on every exposed table. After it is on, the publishable key sees nothing until you add policies. Adding a policy does not, by itself, undo wide grants that already exist. On projects that still grant anon and authenticated by default, revoke the grants you do not want. A signed-in user should not be able to read another user's rows. Lovable and Bolt will create tables for you. Open the Supabase table list and check the lock, or paste the prompt.

Audit Supabase access in this project. Do not run SQL against production and do not change policies yet.

1. List every table in an exposed schema, usually public.
2. For each table, say whether row level security is enabled.
3. List the policies, and say what the anon and authenticated roles can select, insert, update, and delete.
4. Search the repo for a service-role key or secret key. Say whether any browser component, VITE_ variable, or NEXT_PUBLIC_ variable can see it.
5. Flag any table where one signed-in user can read another user's rows.
Stop after the report.

3. Take Firebase out of Test mode

When you create a database, Firebase lets you deny everyone (Locked mode) or grant access to all users (Test mode). The insecure-rules guide shows allow read, write: if true as the pattern that lets anyone overwrite the database, and the same warning for Realtime Database and Cloud Storage. It also says a rule that only checks request.auth != null lets any logged-in user touch data that is not theirs. Open the Rules tab. If you see if true, replace it with a rule that matches the owner's user id before you publish the app. The pages checked here describe that open access. They do not publish a countdown, so this guide does not invent one.

Audit Firebase Security Rules in this project. Do not deploy rules and do not change production.

1. Find every firestore.rules, storage.rules, and database.rules file. If the rules are not in the repo, say that I have to open the Firebase console.
2. Flag any rule that uses "if true", allows read and write with no condition, or sets ".read" or ".write" to true.
3. Flag any rule that allows every signed-in user (request.auth != null, or auth != null) without checking that the user owns that document or file.
4. Say what a stranger who is not logged in can read or write.
Stop after the report.

4. Check the user on the server

A login screen is not authorization. Hiding a button still leaves the request. Every route that reads or writes a row should know who is calling, from the session, and should refuse rows that belong to someone else. Database rules are the backstop. They are not a reason to skip the check in your own server code, especially for an action the browser triggers with a user id it chose.

Audit authorization in this project. Do not change code yet.

1. Find every server route, server action, and edge function that reads or writes user data.
2. For each one, say whether it checks that the caller is signed in, and whether it checks that the caller owns the row.
3. Flag any query that trusts a user id sent from the browser without comparing it to the session.
4. Flag any secret key imported by a file that also renders in the browser.
Stop after the report.

5. Verify the Stripe webhook signature

Stripe signs each webhook with the Stripe-Signature header. Verify it with Stripe's library, the raw body, and the endpoint's signing secret (it begins with whsec_). If the framework parses the body first, verification fails or, worse, you skip it and trust a forged "payment succeeded" event. The secret is different for test and live, and it must not use a VITE_ or NEXT_PUBLIC_ name. Do this before the payments guide becomes a live charge.

Audit the payment webhook in this project. Do not change code and do not call the payment provider.

1. Find the webhook handler.
2. Say whether it verifies the Stripe-Signature header with Stripe's library, using the raw request body and a signing secret.
3. Say whether that secret is read from a server-only variable, or from a VITE_ or NEXT_PUBLIC_ variable.
4. Say whether the handler marks an order paid only after verification succeeds.
Stop after the report.

6. Keep a backup the agent cannot eat

Supabase's pricing page says automatic backups are not included on Free. Pro keeps daily backups for 7 days. Render's free Postgres docs say those databases have no backups, expire 30 days after creation, and are deleted after a further 14-day grace period. If the only copy of your users is in a project an agent can edit, you do not have a backup. Turn on the provider's backup, or export a copy to a place the agent is not logged into, before you invite users. The pricing pages checked on 2026-10-22 do not state a Firebase backup quota, so this guide does not invent one. Check the Firebase console.

Report how this project is backed up. Do not delete, export, or modify production data.

1. Name the database, and say whether a separate development project exists.
2. Say whether the repo shows that automatic backups are turned on. If the repo cannot tell you, say that I have to check the provider dashboard.
3. List which files or tools in this workspace can see the production database password or service-role key.
Stop after the report.

7. Keep the agent away from production data

In July 2025 a Replit user's database lost data while the agent was working. Replit's own write-up says the agent deleted that data, development and production were not separated, and rollback did restore it. Replit says separate development and production databases are now the default, and that the agent cannot change production during development. Other tools do not have that switch. Give the agent a development project. Keep production credentials out of the chat and out of the .env file the agent can read. Paste this before a risky session.

Before you change anything in this project:

1. Tell me whether the database credentials in this workspace point at production.
2. If they do, stop. Do not run migrations, deletes, or updates against that database.
3. Tell me what a separate development project would need, and which credentials I should swap in.
4. Do not create, drop, or empty any table until I confirm you are on the development project.

After the report

Read it. If a secret was in the browser bundle, create a new key in the provider's dashboard and delete the old one. Changing the env var on the host does not erase a key that was already compiled into a deployed file. Then ask the tool to fix one item, review the diff, and run the same prompt again. A second person's account, or the first real charge, waits until the seven items above are true. Building the prototype in a browser builder is still a reasonable start. The SaaS guide is the point where that prototype needs these checks.

Questions people actually ask

Is an app built with AI secure?

Not because it runs. The usual holes are a secret key compiled into the browser, a Supabase table with row level security off, Firebase rules left in Test mode, and a payment webhook that trusts any POST. Each one is a setting or a line of code. The checklist on this page is the pre-launch pass, and each item has a prompt you can paste into the AI tool so it reports what it finds before it edits.

Are VITE_ and NEXT_PUBLIC_ variables secret?

No. Vite's docs say variables prefixed with VITE_ are exposed in client-side code and should not contain secrets such as API keys, because the values are bundled into the source at build time. Next.js docs say a NEXT_PUBLIC_ variable is inlined into the JavaScript sent to the browser. Removing it from the host's dashboard does not remove it from a build that already shipped. If a secret was in one of those names, rotate the key after you move it server-side.

What did Replit's agent do to a production database?

In July 2025, Replit's own post said the agent deleted data from a user's database during development, and that development and production were not separated at the time. Replit says the user restored the data with rollback, and that the agent could not see that rollback existed. Replit later turned on separate development and production databases by default so the agent cannot change production during development. The lesson for any tool: do not give the agent the production password, and do not keep the only copy of the data in the project it is editing.

Sources

Other guides

Take the quiz · When you hit a limit · How the weights work