Case study

Case study: a Supabase paywall bypassed in two requests

On a subscription crypto-education platform, the free sign-up page was enough to unlock the paid content. Here is the technical chain, what held, the fixes and the lesson for AI agents.

By · Updated 2026-10-29

Key figures

ItemFinding
Target typeSubscription crypto-education platform (Next.js + Supabase)
Entry pointA free sign-up, auto-confirmed, with no e-mail verification
ImpactAll premium content readable without paying, including time-sensitive alerts
Requests needed2 (create an account, read the content table)
What heldAnonymous writes blocked by RLS, profiles, subscriptions and watchlists protected, no file storage exposed
Fix priorityImmediate for the RLS policy, within a week for the rest
In short: on a subscription crypto-education platform, the free sign-up page was enough to unlock all the paid content. The database checked that the user was "logged in", never that they were "subscribed". The audit was external and passive, followed by a responsible disclosure to the owner. No data was downloaded.

What exactly happened on this platform?

A subscription crypto-education platform let any visitor read its members-only content. The bypass took two requests: create a free account, then query the content table with the token it returned.

The framing matters. This was a passive external audit, carried out in early 2026 with no written mandate and no paid engagement, on publicly reachable elements. There was no intrusion, no exploitation beyond confirming readability, and no data downloaded. To compare anonymous and logged-in access, a single free test account was created with a generic address, then signed out and its token invalidated at the end. The findings were then sent to the owner as a responsible disclosure, with the list of fixes and the test account to delete.

This case study is anonymized: neither the platform, its leaders nor its domain are named. The pattern itself is very common on applications built with Supabase.

Why would an e-mail capture page unlock the paid course?

Because the free sign-up issues a real authentication token, and that token opens more than the owner assumed. The sign-up page existed to collect e-mail addresses, which is a marketing tool. Technically, it is an entry point into the database.

The owner believed the paywall protected the content. In reality it only protected the display: the site showed "resource not found" to non-subscribers, yet the database API returned the full text to any logged-in account. A paywall that lives in the interface can be bypassed by talking to the API directly.

That is the lesson for a business owner: every page that creates an account is a page that hands out a token. Ask what that token lets someone read, beyond what the screen shows.

What is the technical chain, step by step?

The chain has three links, and it only takes one solid link to break it.

  1. Open sign-up with automatic confirmation. The authentication service accepted any address and validated the account immediately, with no verification e-mail and no CAPTCHA.
  2. Over-broad RLS policy. The row-level security (RLS) rule on the content table checked that the role was "authenticated". It did not check that the user had an active subscription.
  3. Premium content in the same table as public content. A simple filter on visibility returned the members-only articles, full text included.
LinkWhat was in placeWhat was needed
Sign-upOpen, auto-confirmedE-mail confirmation, CAPTCHA, or sign-up tied to payment
RLS policy"Logged in" is enough"Active subscription" required for members visibility
Data modelPublic and premium mixed with no entitlement checkEntitlement checked server-side on every read

One point deserves emphasis: fixing sign-up alone is not enough. Even with e-mail confirmation, anyone with a real address is still "authenticated". The decisive fix is the RLS policy.

What was well secured?

The foundation was healthy, which makes the flaw all the more instructive. These points held:

  • Writes blocked: anonymous insert attempts were rejected by RLS on the tables tested.
  • Personal data protected: profiles, subscriptions and watchlists returned nothing to anonymous visitors, and a logged-in account only saw its own rows.
  • No exposed storage: no publicly readable file bucket.
  • Public key handled correctly: the "anon" key visible in browser code is meant to be public. It is not a flaw: the security boundary is RLS.

The team had done the hard part. It missed one question: "who is allowed to read this row?"

What other findings did the audit raise?

The other findings, ranked by severity, are secondary to the bypass, but they tell the same hygiene story.

SeverityNature of the finding
CriticalPaywall bypass using a free account
HighOpen sign-up with automatic confirmation (root cause)
HighConfiguration table readable without authentication, revealing the pricing structure
To verifyWrite rights on that table not confirmed, left for the owner to check: I deliberately did not test on real rows
MediumWeak e-mail authentication (sender spoofing possible), error-tracking identifier exposed, content security policy header missing
LowPublic test and production keys mixed together, DNS without signing, bulk reading of already-public articles
InfoSubdomains visible through certificates, standard third-party tools

The write question on the configuration table illustrates a rule of the trade: when a test would risk altering real data, you stop and flag the doubt to the owner instead of resolving it yourself.

How do you fix this kind of flaw?

The fix is short, and the RLS policy comes first. Here is the recommended order.

  1. Tie reads to subscription status. The RLS policy must require an active subscription for any row marked "members". The entitlement is checked in the database, not in the interface. See the Supabase Row Level Security documentation.
  2. Close automatic sign-up. Turn on e-mail confirmation (see the Supabase e-mail auth guide), add a CAPTCHA, or make account creation depend on payment.
  3. Check entitlements server-side. For valuable content, go through a server function that verifies the subscription on every read instead of exposing the table.
  4. Do not expose the configuration table. Serve settings from the server, or limit reads to the roles that need them.
  5. Check UPDATE and DELETE. A blocked INSERT does not prove the other operations are blocked.
  6. Harden the perimeter. Strict e-mail authentication, a content security policy, test keys removed from production builds.

These points fall under the broken authorization families described by OWASP, notably API1 (object-level authorization) and API5 (function-level authorization). To go deeper on RLS pitfalls, read our guide to RLS mistakes and the five configuration errors that bypass a paywall.

What does this have to do with AI agents?

An agent or chatbot connected to the same database with a user's token inherits exactly the same over-broad read. If the database answers "yes" to any logged-in account, the agent will answer "yes" too, and a malicious user can ask it to summarize, quote or forward whatever the policy lets through.

This is the core risk of agents: the model is not an access control. A prompt instruction such as "only show premium content to subscribers" is a soft rule that prompt injection can bypass. The entitlement must be checked outside the model, in the database or in an authorization layer that decides before the tool call.

For an agent connected to your data, test at least:

  • what a free account, or another customer's account, can make it read;
  • whether the agent's tools carry the user's token or a broader service key;
  • whether entitlements are rechecked on every call, not only at session start.

These checks match the surface described on our page about AI agent security, and they are the ground covered by an AI agent red team. The article on data isolation for a multi-tenant agent covers the same problem on the agent side, and the n8n webhook case study shows another chain where a single missing check was enough.

How can CleanIssue help?

If your product combines user accounts, paid content and an AI agent, the question to ask is simple: can a free account read what it should not, directly or through the agent? The Agent Red Team Express answers that in 5 days for €1,900, on one specific agent, with a written report and reproducible evidence. It is always paid: there is no "nothing found, nothing paid" guarantee.

The first contact is a free 30-minute discovery meeting. It is used to scope the work before any proposal.

Other case studies

Frequently asked questions

Was this audit authorized?

It was a passive external audit with no written mandate and no paid engagement, limited to publicly reachable elements. A single free test account was used to compare anonymous and logged-in access, then signed out and its token invalidated. The findings were sent to the owner as a responsible disclosure.

Was any user data downloaded?

No. Readability of the content was confirmed and rows were counted, then testing stopped. No premium content or personal data was downloaded or kept, and temporary files were deleted.

Why could a free account read the paid content?

The RLS policy on the content table checked that the user was authenticated, not that they had an active subscription. Since sign-up was open and auto-confirmed, anyone got a valid token in one request.

Does turning on e-mail confirmation fix the problem?

No. Someone with a real address is still authenticated after confirming. The decisive fix is an RLS policy that requires an active subscription for members-only content, with e-mail confirmation and CAPTCHA only reducing mass account creation.

Is a Supabase anon key visible in the browser a vulnerability?

No, that key is designed to be public. The real security boundary is RLS: if the policies are correct, the public key only reaches what you decided to expose.

What is the risk for an AI agent connected to the same database?

If the agent uses a user's token, it inherits the same over-broad read and can summarize or forward whatever the policy lets through. The entitlement must be checked outside the model, in the database or in an authorization layer placed before the tool call.

Related reading

Sources

Know what your agent can do before an attacker does.

Tell us about your agent, its tools and your context. A free 30-minute discovery call.

Book a call