What is Supabase
Supabase is an open-source backend service and a common alternative to Firebase. It is built on PostgreSQL and helps developers create and run applications without needing to build a backend from the beginning.
Supabase makes it easy to build apps by giving you a ready-to-use database, user login, and authentication, along with automatic APIs, file storage, real-time updates, and serverless functions. It even supports AI features like vector storage.
One of the main advantages of Supabase is that it gives developers more control. Unlike closed platforms, it is based on open standards and can be self-hosted. This means developers fully own their data and are not locked into one provider. Because it is easy to use and offers a free tier, Supabase is popular for web, mobile, and AI applications.
Tokens in Supabase and Their Roles
Supabase heavily relies on JSON Web Tokens (JWTs) for secure authentication and authorization. Here's an overview of the key tokens and their roles:
Access Token (JWT)
Service Role Key
This is a secret API key with very high permissions. It completely bypasses Row Level Security (RLS) and works as the service_role user in PostgreSQL. It should only be used on the server side when full access to the database is required.
Never expose this key in frontend or client code, as it grants full, unmitigated control over the database.
Anon (Anonymous/Public) Key:
This is a public API key used for requests that do not require a logged-in user. It works under the anon role and can only access data allowed by your RLS rules (for example, reading public data).
These keys allow Supabase services to communicate securely without keeping session state. To stay secure, always keep secret keys on the server, enable RLS on your tables, and use short expiration times for access tokens.

It All Started With JavaScript (JS)
During penetration testing, one of the first and most effective steps is reviewing JavaScript files. This includes downloading and analyzing all client-side scripts such as bundled files, source maps, and static assets. These files often contain information that developers may accidentally expose.
By carefully searching through JavaScript files, testers can find important details like API endpoints, configuration values, hardcoded secrets, API keys, authentication tokens, and backend URLs.
While going through the JavaScript code and related files, we found a Supabase project URL in the format https://<project-ref>.supabase.co along with its API key.
This finding shows how important it is to secure frontend code properly. Sensitive information should never be included in source code, and access to backend services must be controlled using server-side logic and strong security configurations.

What Next?
After carefully debugging and researching, including looking at online resources, developer blogs, and the official Supabase documentation, we were able to see how the exposed Supabase URL and API key in the JavaScript files could be used.
This research showed that the exposed URL and key allow direct access to the database through Supabase’s auto-generated REST API. Using this API, it is possible to interact with database tables by calling endpoints that follow common naming patterns, such as:
1GET /rest/v1/
2GET /rest/v1/users
3GET /rest/v1/calls
4GET /rest/v1/notifications
5GET /rest/v1/organizationsThese endpoints usually map directly to database table names, which makes it easier for an attacker to discover and interact with backend data if proper security controls are not in place.

These endpoints follow the standard pattern:
GET https://<project-ref>.supabase.co/rest/v1/
To query these endpoints, requests must include the following headers:
Host: <project-ref>.supabase.co
apikey: <exposed-anon-key>
Authorization: Bearer <exposed-anon-key>
When these GET requests are sent in the correct format, they return JSON responses that contain data from the related database tables.
One important thing about Supabase is that it is designed to allow direct access to the database from the client side, including web browsers, using the exposed API key. This design allows developers to perform full database actions directly from frontend code, without needing a backend server.
These actions include:
Create (POST)
Read (GET)
Update (PATCH / PUT)
Delete (DELETE)
Because of this, the same requests can also be sent manually using tools like curl or directly from the browser’s developer console. If access rules are not configured correctly, this behavior can lead to serious security risks.

"This approach also allowed us to query the database schema. "

After reading further into the documentation of supabase, we found something called the select parameter.
Select all columns : select=*
Select specific columns : select=id,email,created_at
We repeated the same approach with different endpoints.
How to Fix
To fix these issues and apply security controls, we first need to define what RLS is.
Row Level Security (RLS) in Supabase?
Row Level Security (RLS) is a security feature built into PostgreSQL, which is the database used by Supabase. It allows you to control access to individual rows in a database table by using rules called policies.
In simple terms:
Without RLS, a user (or API key) with access to a table can potentially see or modify all rows in that table.
With RLS enabled, you define policies essentially custom SQL conditions that automatically filter which rows a query can read, insert, update, or delete.
These policies act like invisible "WHERE clauses" added to every query, enforcing restrictions at the database level.
Why RLS is Critical in Supabase
Supabase encourages direct client-side access to the database using the public anon key . This key is intentionally public and safe to use in browsers only if RLS is properly configured.
If RLS is disabled on a table: Anyone with the anon key can perform full CRUD operations on all data (huge vulnerability if keys/URLs are exposed).
If RLS is enabled but no policies exist: The table becomes completely inaccessible (even to legit users) queries return empty results.
If RLS is enabled with good policies: Access is restricted like users see only their own data, public data is readable by anyone, admins have broader access.
How it Works
Enable RLS:
Create Policies
Important Recommendations
To protect your data, the first step is to enable and properly configure Row Level Security (RLS). If any sensitive information was exposed, you should also rotate the affected API keys through the Supabase dashboard and check logs for any suspicious activity.
Enable Row Level Security (RLS) on All Tables:
Log in to your Supabase dashboard and go to the Database section. For each table (like users, calls, notifications, organizations), turn RLS on.
Define policies that control who can access each row. Example: Only allow authenticated users to read their own data:
Why this fixes it: RLS applies rules directly in the database, so even if an API key is exposed, unauthorized queries will be filtered or blocked.
Rotate Exposed Keys if Necessary:
Service Role Key (high-privilege, secret): If it was exposed, make a new key in the Supabase dashboard and update your server code.
Anon Key (public-facing): This key is meant to be public, but if it was over-shared, make a new one just to be safe.
Best Practices: Always store keys in environment variables or a secure vault. Never put them in frontend code.
Minimize Exposed Keys:
Always remove or hide API keys from your JavaScript code when building your app. Use environment variables in frameworks like React or Vue instead of hardcoding them. Never put service keys in frontend code, because they bypass RLS and should only be used on the server.



