# Before you go live: a security checklist for apps built with AI

Checked on 1 November 2026

Before you launch an app built with AI, check eight things: who can read and change each database table, secret keys in the browser code, email confirmation on sign-up, payments and paid features decided on the server, a usage limit on any AI helper, rules for file uploads, admin pages checked on the server, and browser protections.

Then run your builder’s own security scan, which reads your code, and an outside check such as LidTight’s free check, which tests the live app as a stranger sees it; fix every critical and high finding before launch.

On 1 November 2026, our free and deep checks on Tidybook, a demo app we built with Lovable, found 2 critical, 1 high and 1 medium problem.

## The checklist in order

1. [Set who can read and change each table](https://lidtight.com/guides/launch-security-checklist#tables).
2. [Keep secret keys out of the browser](https://lidtight.com/guides/launch-security-checklist#secret-keys).
3. [Turn on email confirmation](https://lidtight.com/guides/launch-security-checklist#signup).
4. [Decide payments on the server](https://lidtight.com/guides/launch-security-checklist#payments).
5. [Limit the AI helper](https://lidtight.com/guides/launch-security-checklist#ai-limit).
6. [Set upload rules](https://lidtight.com/guides/launch-security-checklist#uploads).
7. [Check admin pages on the server](https://lidtight.com/guides/launch-security-checklist#admin).
8. [Turn on browser protections](https://lidtight.com/guides/launch-security-checklist#headers).
9. [Run your builder’s scan](https://lidtight.com/guides/launch-security-checklist#builder-scan).
10. [Check the live app from the outside](https://lidtight.com/guides/launch-security-checklist#outside-check).

## 1. Set who can read and change each table

This step uses Supabase, which Lovable’s built-in backend [is built on](https://docs.lovable.dev/features/cloud). Row level security (RLS) is a set of rules in the database that decide who may read and change each table’s rows. The public key in your page [only reaches what RLS allows](https://supabase.com/docs/guides/getting-started/api-keys), and a table without RLS is [readable and writable](https://supabase.com/docs/guides/database/postgres/row-level-security) by anyone who has access to it, often signed-out visitors too.

1. Write one line per table: who may read it, and who may change it.
2. Compare with the rules: in Lovable, More → Database → Manage → Policies ([Lovable](https://docs.lovable.dev/tips-tricks/security-best-practices)); in your own Supabase project, the Security Advisor flags tables with RLS off and rules open to everyone ([Supabase](https://supabase.com/docs/guides/observability/advisors)). On Base44, see [Base44 app security](https://lidtight.com/guides/base44-app-security).
3. A rule for signed-out visitors (“anon”) with “using (true)” [gives them every row](https://supabase.com/docs/guides/database/postgres/row-level-security). Keep it only for public data.
4. Sign in as two test users and check that neither sees the other’s rows ([how](https://lidtight.com/guides/one-customer-sees-another)).

On Tidybook, anyone could read 12 review rows with emails and phone numbers, without signing in (critical). See [what a stranger can see](https://lidtight.com/guides/what-strangers-see).

## 2. Keep secret keys out of the browser

Anyone can read what the browser downloads, so Lovable says secrets in frontend code [should be considered compromised](https://docs.lovable.dev/tips-tricks/security-best-practices). A Supabase secret key (sb_secret_…, or the older service_role key) [bypasses every RLS rule](https://supabase.com/docs/guides/getting-started/api-keys). Keys for paid services, such as a Stripe secret key (sk_live_…), belong in server code only ([Lovable](https://docs.lovable.dev/tips-tricks/security-best-practices)).

1. Search your project’s code for sk_live_, sb_secret_, service_role and secret.
2. A setting whose name starts with VITE_ is [copied into the browser code](https://vite.dev/guide/env-and-mode); Supabase [warns the same](https://supabase.com/docs/guides/getting-started/api-keys) for NEXT_PUBLIC_. Never put a secret under such a name.
3. Open your live app’s developer tools and search the downloaded files for the same words.
4. If a secret key was ever public, [rotate it](https://supabase.com/docs/guides/getting-started/api-keys): make a new key and switch the old one off.

The Supabase publishable key (sb_publishable_…, or the older anon key) is [safe to expose](https://supabase.com/docs/guides/getting-started/api-keys); [here is why](https://lidtight.com/guides/supabase-public-key). On Tidybook, the check found no secret keys.

## 3. Turn on email confirmation

Without email confirmation, [anyone can sign up with an address they don’t own](https://docs.lovable.dev/features/email-auth), and a bot can create many fake accounts that use your free limits.

1. Lovable’s built-in backend: in More → Users → Settings → Email, turn off Auto-confirm email ([Lovable](https://docs.lovable.dev/features/email-auth)).
2. Your own Supabase project: turn on Confirm email in the email provider settings ([Supabase](https://supabase.com/docs/guides/auth/general-configuration)).
3. Test it: sign up with your own address. You should not get in before you click the emailed link.

On Tidybook, sign-up was open with no email confirmation, a high finding. More on [sign-up without email confirmation](https://lidtight.com/guides/signup-without-email-confirmation).

## 4. Decide payments and paid features on the server

Users [can inspect or change](https://docs.lovable.dev/tips-tricks/security-best-practices) code that runs in their browser. If your page writes “paid”, “plan”, “credits” or “role” to the database, a user can too. Stripe says to fulfill orders in a server function started by a webhook (a message Stripe sends your server after a payment), since the customer [may never reach your thank-you page](https://docs.stripe.com/checkout/fulfillment).

1. Ask your AI tool which code sets paid, plan, credits or role, and whether it runs in the browser or on the server.
2. Move each such change into a server function that first checks the payment with Stripe.
3. Check that a normal user cannot change those fields, even on their own row.

On Tidybook, a test user changed its own booking from “unpaid” to “paid” with one request, without paying (critical). More on [paid features and admin rights for free](https://lidtight.com/guides/paid-features-for-free).

## 5. Put a usage limit on any AI helper

Each call to an AI service costs you money. Without a limit, one bot can run up your bill.

1. Let only signed-in users call the AI helper, through a server function.
2. Count calls per user on the server and refuse calls over your limit.
3. As a last guard, set a spend limit with the AI provider: OpenAI has [a hard spend limit](https://developers.openai.com/api/docs/guides/production-best-practices), and Anthropic [a monthly spend limit](https://platform.claude.com/docs/en/api/rate-limits).

On 31 October 2026, an earlier run of the deep checks called Tidybook’s AI helper 5 times in 3 seconds, without signing in. All 5 calls were answered (high). More on [AI bill abuse](https://lidtight.com/guides/ai-bill-abuse).

## 6. Set rules for file uploads

If anyone can upload, a stranger can fill your storage at your cost. If a storage bucket (a folder for files) is public, [anyone with a file’s link can open it](https://supabase.com/docs/guides/storage/buckets/fundamentals).

1. Keep buckets private, as [Supabase does by default](https://supabase.com/docs/guides/storage/buckets/fundamentals), unless every file is meant for everyone.
2. Allow uploads only for signed-in users, into their own folder. Supabase allows [no uploads without a rule](https://supabase.com/docs/guides/storage/security/access-control); check that no rule lets everyone in.
3. Set a maximum file size and the allowed file types [on each bucket](https://supabase.com/docs/guides/storage/buckets/fundamentals).

More on [uploads at your cost](https://lidtight.com/guides/file-upload-abuse).

## 7. Check admin pages on the server

Hiding the admin button does not protect the admin page: its address is in the code every visitor downloads (on Tidybook, /admin). The OWASP Top 10 list of web risks says access control only works in [trusted server-side code](https://top10.owasp.org/2021/A01_2021-Broken_Access_Control/).

1. Check the admin role in the table rules or a server function, never only in the page ([Lovable](https://docs.lovable.dev/tips-tricks/security-best-practices)). Keep that role where users cannot change it, even on their own row ([how](https://lidtight.com/guides/paid-features-for-free)).
2. Sign in as a normal test user, open the admin page, and check that it loads no data.

## 8. Turn on browser protections

Security headers are settings your app sends to tell the browser what to block. A content security policy (CSP) mainly defends against [cross-site scripting](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP), where an attacker runs their own code in your page. Its [frame-ancestors](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-ancestors) rule, or the [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Frame-Options) header, stops other sites from showing your page inside theirs to trick users into clicks.

1. Run [Mozilla’s HTTP Observatory](https://developer.mozilla.org/en-US/observatory) on your app’s address to see which headers are missing.
2. Ask your AI tool or your host to add them, then run the test again.

Mozilla says a top grade [does not mean a site is secure](https://developer.mozilla.org/en-US/observatory/docs/faq). Tidybook had no CSP and no framing protection (medium). More on [security headers and CORS](https://lidtight.com/guides/security-headers-cors).

## 9. Run your builder’s own security scan

Lovable, Bolt and Base44 each have a built-in scan that reads your project from the inside.

- **Lovable.** The Quick scan runs each time you publish and checks your database rules and known flaws in your code packages. The Deep scan reviews all your code, including access control and payments; it is free, but you start it yourself in the Security view ([Lovable](https://docs.lovable.dev/features/security)).
- **Bolt.** Publish → Run security audit on paid plans; a database security check on all plans ([Bolt](https://support.bolt.new/building/security)).
- **Base44.** Dashboard → Security → Run Security Scan, on all plans ([Base44](https://docs.base44.com/Setting-up-your-app/running-a-security-scan)).

What it cannot see: Lovable says its built-in scanners [read your code and settings](https://docs.lovable.dev/features/security) and [do not replace a thorough security review](https://docs.lovable.dev/features/security). Testing the running app is a separate add-on billed by a partner. More on [whether the builder’s scan is enough](https://lidtight.com/guides/is-builder-scan-enough).

## 10. Check the live app from the outside

An outside check sees what a stranger sees: the code the browser downloads, and what your database and server answer to anyone. [LidTight’s free check](https://lidtight.com/#check-now) does this from your app’s link in seconds, with no sign-up. It looks for secret keys, tables and server addresses open to anyone, sign-up without email confirmation, secret files, public source code, and browser and email protections. It counts records and keeps no customer data.

What it cannot see: your server code and anything behind sign-in. For those, the free deep checks sign up two test users and test steps 1, 4, 5 and 6, plus admin rights from step 7. On Base44 apps, the sign-up check and the deep checks do not work yet.

### Example: Tidybook, 1 November 2026

Tidybook is our demo app, built with Lovable. In 15.1 seconds, the free check and the deep checks found 2 critical, 1 high and 1 medium problem.

| Severity | What the check found | Step |
| --- | --- | --- |
| Critical | Anyone can read your reviews table: 12 rows, with first_name, email, phone | 1 |
| Critical | An order in bookings can be marked as paid without payment (deep checks) | 4 |
| High | Anyone can create accounts without confirming an email | 3 |
| Medium | Some browser protections are switched off. Missing: content security policy, protection against framing | 8 |

They found no secret keys or secret files. A second test user could not see the first one’s booking, and the server blocked changes to another user’s data.

> LidTight wrote this guide; every step except the last works without it. Run both scans again after each fix, and whenever you change sign-in or data access.

## Sources

1. [Supabase: API keys](https://supabase.com/docs/guides/getting-started/api-keys)
2. [Supabase: Row Level Security](https://supabase.com/docs/guides/database/postgres/row-level-security)
3. [Supabase: Advisors](https://supabase.com/docs/guides/observability/advisors)
4. [Supabase: Auth general configuration](https://supabase.com/docs/guides/auth/general-configuration)
5. [Supabase: Storage access control](https://supabase.com/docs/guides/storage/security/access-control)
6. [Supabase: Storage buckets](https://supabase.com/docs/guides/storage/buckets/fundamentals)
7. [Lovable: Security overview](https://docs.lovable.dev/features/security)
8. [Lovable: Security best practices for Lovable apps](https://docs.lovable.dev/tips-tricks/security-best-practices)
9. [Lovable: Email authentication for your app](https://docs.lovable.dev/features/email-auth)
10. [Lovable: Built-in backend (Cloud)](https://docs.lovable.dev/features/cloud)
11. [Bolt: Check your project’s security](https://support.bolt.new/building/security)
12. [Base44: Running a security scan](https://docs.base44.com/Setting-up-your-app/running-a-security-scan)
13. [Vite: Env variables and modes](https://vite.dev/guide/env-and-mode)
14. [Stripe: Fulfill orders](https://docs.stripe.com/checkout/fulfillment)
15. [OpenAI: Production best practices](https://developers.openai.com/api/docs/guides/production-best-practices)
16. [Anthropic: Rate limits and spend limits](https://platform.claude.com/docs/en/api/rate-limits)
17. [OWASP Top 10:2021, A01 Broken Access Control](https://top10.owasp.org/2021/A01_2021-Broken_Access_Control/)
18. [MDN: Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP)
19. [MDN: CSP frame-ancestors](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-ancestors)
20. [MDN: X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Frame-Options)
21. [Mozilla HTTP Observatory](https://developer.mozilla.org/en-US/observatory)
22. [Mozilla HTTP Observatory FAQ](https://developer.mozilla.org/en-US/observatory/docs/faq)
