Is Your Website Secure? The Data-Leak Check Most Checkers Skip

Is this website secure? A padlock and malware scan only check the surface. See the data-leak check (missing RLS) most website security checkers skip entirely.

Barret8 min read

Type "is this website secure" into a search bar and every tool that comes back checks the same three things: is there a padlock, is the domain on a malware blocklist, and does it show up in a phishing database. Those checks matter. They also stop well short of what most people are actually asking, which is closer to "if I use this, is my data safe."

A site can pass every one of those checks and still hand a stranger every row of your users table. HTTPS protects data in transit, between the browser and the server. It says nothing about what happens once that data lands in the database, or who's allowed to read it back out. "Secure connection" and "secure data" are two different claims, and most checkers only test the first one.

This matters more than usual now because most new apps are built with AI tools on Supabase, the default database behind Lovable, Bolt, v0, and similar platforms. Supabase puts a single control between a stranger and your data: Row Level Security (RLS). Get that control wrong and no padlock or malware scan will catch it from the outside. This post covers what "secure" actually means, why RLS is the check a padlock can't see, and how to test it on your own app.

⚡ TL;DR

  • A padlock and a malware blocklist confirm a secure connection and a clean reputation. Neither one confirms your data is protected once it reaches the database.
  • Apps built with AI tools default to Supabase, where Row Level Security (RLS) decides who can read which rows. Missing RLS, or a policy written as USING (true), means anyone with the public key can read everything.
  • CVE-2025-48757 found this exact gap in 170 of 1,645 scanned Lovable/Supabase apps (10.3%), reachable through 303 endpoints with nothing but the public anon key.
  • The Supabase anon key showing up in your page source is not the problem. A missing or permissive RLS policy behind it is.

Is this website secure? What that actually means

"Secure" gets used for at least three separate guarantees, and conflating them is how the padlock ends up doing more work than it should.

Transport security is what HTTPS provides: the connection between a visitor's browser and your server is encrypted, so nobody on the network in between can read or tamper with it. Reputation security is what malware and phishing checkers confirm: the domain isn't on a known blocklist for malicious downloads or brand impersonation, useful for spotting a scam site. Neither one says anything about a legitimate site's own database.

Data security is the one nobody's padlock checks: once a visitor logs in, does the database only return rows that visitor is allowed to see. This is enforced entirely server-side, invisible from a browser's address bar, and it's where the real damage happens. A checker answering "is this a secure site" from the outside can verify the first two categories in seconds. It cannot verify the third without probing how the app actually behaves.

This gap matters more for AI-built apps than for a typical hand-coded site, because most AI coding tools reach for Supabase, the fastest path from prompt to working database. Supabase security is functionally the same problem as vibe-coding security for most of the market. Instead of a server deciding what data to send back, the frontend often talks straight to the database using a public API key, and rules inside the database itself decide what that key can see. Those rules are Row Level Security, the control standing between "each user sees their own data" and "anyone can read the whole table." A checker scanning HTTPS, headers, and blocklists has no way to see whether those rules exist, because RLS lives inside the database, not in anything fetchable from outside.

What Row Level Security actually does, and where it fails

RLS is a per-user filter the database applies to every query, automatically, regardless of what the frontend asks for. Configured correctly, a request for "all rows in the orders table" from a logged-in user only returns that user's own orders, even if nothing in the frontend filtered by user ID.

The failure mode is almost always one of two things: RLS disabled on a table entirely, so any query returns every row to anyone holding the public key, or RLS enabled with a policy that reads USING (true), which sounds like a rule but evaluates to "let everyone through." It's the security equivalent of a locked door with the key taped to it. This exact trap shows up constantly in AI-generated schemas, because an AI assistant asked to "add a policy so the app works" will often write the version that makes the error go away rather than the version that actually restricts rows.

Either way, the result looks identical to a checker that only reads public files: the site loads fine, HTTPS is green, and nothing in the HTML or headers hints the database behind it is wide open.

CVE-2025-48757: what happens when this goes live

This isn't a hypothetical. CVE-2025-48757, disclosed in May 2025 by researcher Matt Palmer, is the first published CVE for exactly this pattern: missing or USING (true) RLS on Supabase-backed apps built with Lovable. The scan behind it found 170 of 1,645 apps checked, 10.3%, leaking data through 303 endpoints, every one reachable with nothing more than the public anon key. No login, no exploit, no credentials stolen. A single request to an endpoint that should have said no, and didn't.

The same shape of mistake, an AI assistant scaffolding a working app without a working access rule behind it, recurs across other AI-coding platforms that ship on Supabase. The full writeup on CVE-2025-48757 covers how the researcher found it.

🐺 Not a real problem: the anon key showing up in your source

If a scanner or a curious visitor finds your Supabase anon key in your page's JavaScript, that is not a leak. It's meant to live in browser code, by design, with RLS doing the actual gatekeeping behind it. Revoking or hiding it doesn't fix anything and can break your app. The real question is never "can someone see this key," it's "what does this key let someone do," and that depends entirely on the RLS policies behind it. A checker that flags the anon key as critical is crying wolf over something public by design.

How to check your own site

You don't need to wait for someone else to find this. Two checks, both doable in a browser, cover the ground a padlock checker can't.

1. Read your own policies. Open your Supabase dashboard, go to Authentication → Policies, or Table Editor → select a table and check its RLS toggle. For every table holding real user data, confirm RLS shows as enabled, then open one of its policies and read the actual rule. If it says USING (true), that table is unprotected no matter what the toggle says.

2. Test it as a stranger would. Log out completely, or open a private browser window. Then send a request straight to your Supabase REST endpoint using only the public anon key, no session token:

curl "https://YOUR_PROJECT.supabase.co/rest/v1/orders?select=*" \
  -H "apikey: YOUR_ANON_KEY" \
  -H "Authorization: Bearer YOUR_ANON_KEY"

If that returns rows belonging to other people, your policy isn't restricting anything, regardless of what the dashboard toggle shows. If it returns an empty array or a permission error, the table is doing its job. A deeper walkthrough of this test covers cases that are easy to miss, like tables that look private but were never given a policy at all.

Want the 60-second version instead?

Paste your link and get a free, read-only report of what an attacker sees on your live site. No login, no install, no code to read.

Scan my site free →

Fixing a missing or permissive policy

If the anonymous test above returned data it shouldn't have, the fix is a policy rewrite, not a rip-and-replace of your backend. Enable RLS on every table holding anything a user would call private (profiles, orders, messages, uploads, billing records), then write a policy that restricts rows to the requesting user, for example USING (auth.uid() = user_id) instead of USING (true). Re-run the curl test after each change: read and write policies are separate, and it's common to lock down SELECT while leaving INSERT or UPDATE open.

FAQ

If my site has a padlock and passes a malware scan, does that mean it's secure?

It means the connection is encrypted and the domain isn't on a known blocklist. Neither check touches how your database decides what to return to which user, which is where an RLS gap lives. Treat the padlock as one passed test, not a verdict.

How do I know a website is secure if I'm only a visitor, not the owner?

As a visitor, the padlock and a quick check that the domain matches who you expect (no misspellings, no unfamiliar subdomain) is what you can verify directly. The RLS-level checks in this post require testing your own backend as a logged-out user, so they're for people who run the site or app, not for someone browsing it.

Is a Supabase anon key in my site's source code a security problem by itself?

No. The anon key is a public identifier, not a secret, and Supabase's whole access model assumes it's visible in the browser. The actual risk sits in the RLS policies behind it, which is why testing those policies directly, rather than searching for the key, is the check that matters.

My app doesn't use Supabase. Does any of this apply to me?

The commands change, but the principle doesn't. Any backend where a public key or token grants direct database access needs an equivalent to RLS, whether that's Firebase Security Rules, a custom API's authorization middleware, or another provider's row-level policies. The test is the same regardless of platform: try to read data you shouldn't have access to, logged out, and see what comes back.

The bottom line

"Is this website secure" has a real answer, but it's bigger than a padlock and a malware check. Those confirm a secure connection and a clean reputation. They can't confirm your database only shows each user their own data, because that control lives inside your backend, invisible from outside. For apps built with AI tools on Supabase, that control is Row Level Security, worth testing directly rather than assuming it's right. Your AI ships new code, and new policies, on every deploy, so this isn't a check you run once. Run it again after anything that touches auth or data.

Is your site hackable? Find out in 60 seconds.

Is My Site Hackable? scans your live app for the exact gaps in this article (exposed keys, missing RLS, open buckets) and tells you what's a real risk and what's a false alarm. Paste your link, get a free report. No login, no install.

Run my free scan →