Apple does not log into your Supabase project. The anon key sits inside your IPA because Supabase designed it that way, but the protections that decide what that key can read or write live in Postgres, not in App Review. If your Row Level Security policies are missing, the green badge from Apple does not catch it.
Short answer
Apple's review process does not exercise your Supabase backend. According to Supabase's documentation on API keys, the anon key is meant to ship in client code and operates under the Postgres anon role. Safety depends on whether each exposed table has Row Level Security enabled with sensible policies. The anon key alone is not the abuse vector. An anon key plus a table with no policies, or a service role key bundled by mistake, is.
What you should know
- The anon key is a project identifier, not a credential. Supabase issues it for use in browsers and mobile apps; it is scoped to the Postgres
anonandauthenticatedroles. - Row Level Security is the gate. When RLS is on, every query is filtered by a policy expression before any row leaves Postgres.
- The service role key is the dangerous one. It carries the
BYPASSRLSattribute and reads everything in the database. - App Review does not run penetration tests. Reviewers exercise the UI on a real device against the App Review Guidelines. Backend authorization stays the developer's responsibility.
- An IPA is a zip file. Anyone with the archive can read the JavaScript or native bundle and pull the anon key out in seconds.
Does Apple actually look at my Supabase configuration during review?
No. The App Review pipeline does not authenticate against your Supabase project, send queries through your REST endpoint, or test whether the anon key can list tables it should not. According to Apple's App Review Guidelines, reviewers focus on user-facing behavior, Privacy Manifest declarations, App Tracking Transparency prompts, entitlement use, and the Safety, Performance, Business, Design, and Legal categories. Guideline 1.6 expects developers to implement security measures, but Apple does not validate those measures at the database layer.
That has a practical consequence: a build with an open Supabase project, a missing RLS policy, or a leaked service role key will usually pass App Review. The badge is not a backend security audit. It is a check that your app behaves on a device the way Apple expects it to behave for a user.
After launch the calculus changes. Under Guideline 5.1.1(vi), apps that surreptitiously expose or collect private data can be removed from the Apple Developer Program. A public incident, a security researcher writeup, or a press story about leaked user data can trigger enforcement long after the initial approval.
What is the real difference between the anon key and the service role key?
Both keys are JSON Web Tokens, signed by your project, with a role claim that tells Postgres which database role to assume. The anon key carries role: "anon". The service role key carries role: "service_role". The two roles behave very differently inside Postgres.
According to Supabase's documentation on API keys, the publishable (anon) key is designed to ship in mobile apps, single-page web apps, CLIs, and any public artifact, with access guarded by Postgres via the built-in anon and authenticated roles. The secret (service role) key uses a role with full access and the BYPASSRLS attribute, meaning Postgres skips every policy check for that connection.
| Key type | Postgres role | RLS applies? | Where it belongs |
|---|---|---|---|
| Anon (publishable) | anon / authenticated | Yes | Mobile app, web bundle, CLI |
| Service role (secret) | service_role | No (BYPASSRLS) | Backend, Edge Function, server |
| Custom JWT | The role the JWT signs as | Yes | Wherever the JWT was minted |
The takeaway is simple. The anon key is fine to embed in a client. The service role key is never fine to embed in a client. Confusing the two, or pasting the wrong one into a .env that ships with the bundle, is the single most common Supabase incident reported in security writeups.
What does Row Level Security actually do when the anon key is exposed?
Row Level Security is a PostgreSQL feature, exposed through Supabase, that adds an implicit WHERE clause to every query against a table. According to Supabase's Row Level Security documentation, once RLS is enabled and at least one policy exists, the database evaluates the policy for the calling role on every read, insert, update, and delete.
That is the protection your shipped anon key relies on. The flow looks like this:
- The app sends a request with the anon key as the bearer token.
- PostgREST authenticates the request and connects to Postgres using the
anonrole. - Postgres looks at the target table, checks RLS, and applies every matching policy.
- Only the rows that pass the policy expression are returned.
If the table has RLS disabled, Supabase emits a warning in the dashboard, and on the current publishable key model PostgREST refuses anon reads by default. According to the documentation, no data will be accessible via the API when using a publishable key until you create policies. The opposite failure mode is more common: RLS enabled with a policy that reads USING (true) for the anon role. The shape is correct; the substance is fully open.
A policy of USING (true) for SELECT on the users table tells Postgres to return every row to every anon caller. That is the most common abuse vector in vibe-coded apps. The fix is to scope policies by auth.uid(), by a tenant id, or by an is_public column.
How does someone actually abuse an exposed Supabase backend?
The flow is short. On macOS, rename the .ipa to .zip, double-click, and open Payload/YourApp.app/. On Android, unzip the .apk. Search for supabase.co in the bundle, in any index.android.bundle or main.jsbundle, or in a Hermes .hbc file decompiled with hermes-dec. The Supabase project URL is usually a few characters away from the anon key, often on the same line.
Once an attacker holds the URL and anon key, every PostgREST endpoint is one curl request away. A GET against /rest/v1/users?select=* returns every row the anon role can read. If USING (true) is in place anywhere, the response is the full table. The OWASP Mobile Application Security Verification Standard groups this under MASVS-AUTH and MASVS-STORAGE: backend authorization must be enforced server side, and sensitive data must respect the threat model.
The point is not the difficulty. The point is that anyone with five minutes can do it. If your RLS is wrong, the cost to find out is small.
What should you actually check before pressing Submit for Review?
A short pre-submission list for any Supabase-backed iOS or Android app:
- Schema exposure. Confirm only the
publicschema is exposed through PostgREST. Move tables that hold staff data, audit logs, or admin records to a private schema. - RLS coverage. Run
select tablename from pg_tables where schemaname = 'public' and rowsecurity = false;and verify the list is empty. - Policy substance. Read every policy by hand. Reject any
USING (true)policy on a table that holds user data, payment data, or anything not designed to be public. - Anon role grants. Audit which tables the
anonPostgres role canSELECTfrom. Drop grants you do not need. - No service role on the client. Search your repository, your
.env.example, your CI variables, and the compiled bundle for the stringservice_role. The only acceptable result is inside server side code or an Edge Function.
For builders shipping AI-coded or no-code projects where the backend was wired up by an agent rather than by a security review, PTKD.com (https://ptkd.com) is one of the platforms focused on pre-submission scanning aligned with OWASP MASVS, including detection of Supabase URLs and key shapes inside compiled iOS and Android builds before they reach App Store Connect.
What to watch out for
A handful of patterns recur in Supabase incident writeups.
The first is treating Apple as a backend auditor. App Review does not query your tables. According to Apple's App Review Guidelines, the reviewer's brief is the user experience, the disclosures, and the policy categories. Backend misconfiguration is invisible to that pass.
The second is rotating the anon key after a leak. The anon key was public by design. Rotating it without fixing RLS only forces an attacker to download the next build and pull the new key. The data exposure does not change.
The third is using the service role key inside the mobile build during early development. A common pattern is to wire up Supabase quickly during a Lovable, Bolt, or Replit Agent prototype with the service role key on the client, then forget to swap it before submission. Once that key is in an IPA, it has shipped. Rotate immediately, then rebuild from the anon key plus RLS.
The fourth is assuming the dashboard RLS-enabled badge is enough. The badge tells you RLS is on. It does not tell you whether the policies behind it are well written. A USING (true) policy passes the badge check and fails the security check.
Key takeaways
- Apple's App Review does not test Supabase Row Level Security. A build with an open backend will often pass review, then expose data to the first person who unzips the IPA.
- The anon key is a public role token guarded by Postgres policies. The service role key bypasses every policy and must never leave a server.
- RLS enabled with a permissive
USING (true)policy is the most common abuse vector in vibe-coded Supabase apps. Read every policy by hand before you submit. - The pre-submission gates are schema exposure, RLS coverage, policy substance, anon role grants, and the absence of the service role key in the bundle.
- For mobile builders who want an external read of a compiled iOS or Android binary, including detection of Supabase keys and obvious RLS hints, PTKD.com (https://ptkd.com) is one of the platforms focused on OWASP MASVS pre-submission scanning.



