Target: zairmalpublcagnbodsi.supabase.co
Date: 2026-03-25
Scope: External reconnaissance & configuration analysis
Endpoint tested: /rest/v1/formas_recebimento?select=codigo,nome&company_id=eq.<uuid>
This audit identifies multiple security risks in a Supabase-backed application exposing a PostgREST API. The primary concerns are around tenant isolation (IDOR), overly permissive CORS, schema leakage, and potential Row-Level Security (RLS) misconfigurations. Without access to the Supabase dashboard or the anon key, findings are based on external observation and known Supabase architecture patterns.
| Field | Value |
|---|---|
| Severity | Critical |
| CVSS | 8.6 (High) |
| Category | OWASP A01:2021 — Broken Access Control |
Description:
The endpoint filters data by company_id as a query parameter:
/rest/v1/formas_recebimento?select=codigo,nome&company_id=eq.e11cdf51-b57a-4fc2-b415-c864dcdaeb26
This is a client-side filter, not a server-side access control. Any authenticated user (or anyone with the anon key) can replace the UUID with another company's ID to access their payment methods.
Impact: Full cross-tenant data access. An attacker can enumerate all companies' formas_recebimento records.
Remediation:
-- Enable RLS on the table
ALTER TABLE formas_recebimento ENABLE ROW LEVEL SECURITY;
-- Create a policy that restricts access to the user's own company
CREATE POLICY "Users can only view their company's data"
ON formas_recebimento
FOR SELECT
USING (company_id = (auth.jwt() ->> 'company_id')::uuid);| Field | Value |
|---|---|
| Severity | High |
| Category | OWASP A05:2021 — Security Misconfiguration |
Observed headers:
access-control-allow-origin: *
access-control-allow-methods: GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS,TRACE,CONNECT
access-control-max-age: 3600
Issues:
Access-Control-Allow-Origin: *— Any website can make requests to this API. A malicious site could make authenticated requests on behalf of a logged-in user (if cookies/tokens are involved).- All HTTP methods allowed — Including
TRACEandCONNECT, which are rarely needed and can be used for cross-site tracing (XST) attacks. PUT,PATCH,POST,DELETEallowed from any origin — If theanonkey is embedded in a frontend, any malicious site can perform write/delete operations.
Remediation:
- Restrict
Access-Control-Allow-Originto your specific frontend domain(s). - Remove
TRACEandCONNECTfrom allowed methods. - Use Supabase custom CORS configuration or an API gateway.
Note: This is Supabase's default CORS configuration. While Supabase relies on API keys + RLS rather than CORS for security, the permissive CORS combined with a client-embedded
anonkey means any website can call your API with valid credentials.
| Field | Value |
|---|---|
| Severity | High |
| Category | OWASP A04:2021 — Insecure Design |
Exposed information from the URL alone:
| Leaked Item | Value | Risk |
|---|---|---|
| Table name | formas_recebimento |
Reveals DB schema — attacker knows table structure |
| Column names | codigo, nome |
Reveals data model — aids further exploitation |
| Tenant ID | e11cdf51-b57a-4fc2-b415-c864dcdaeb26 |
Real UUID — enables IDOR enumeration |
| Language/locale | Portuguese table/column names | Reveals target market/geography |
Additional risk: PostgREST exposes an OpenAPI schema at /rest/v1/ when accessed with a valid API key. If the anon key is public (embedded in frontend), an attacker can discover every exposed table, column, and relationship in a single request.
Remediation:
- Use Supabase Edge Functions or a backend API to proxy requests, hiding the direct PostgREST interface.
- Restrict which tables are exposed to the
publicschema using PostgreSQL roles. - Consider using database views with limited columns instead of exposing raw tables.
| Field | Value |
|---|---|
| Severity | Medium |
| Category | OWASP A05:2021 — Security Misconfiguration |
Present headers (good):
x-content-type-options: nosniff✅strict-transport-security: max-age=31536000; includeSubDomains; preload✅
Missing headers:
| Header | Purpose | Status |
|---|---|---|
Content-Security-Policy |
Prevents XSS and data injection | ❌ Missing |
X-Frame-Options |
Prevents clickjacking | ❌ Missing |
X-XSS-Protection |
Legacy XSS filter | ❌ Missing |
Referrer-Policy |
Controls referrer leakage | ❌ Missing |
Permissions-Policy |
Restricts browser features | ❌ Missing |
Note: Some of these are less relevant for a pure API endpoint but become critical if any HTML responses are served.
| Field | Value |
|---|---|
| Severity | Medium |
| Category | OWASP A04:2021 — Insecure Design |
Observation:
GET /storage/v1/object/public/
→ 400: {"statusCode":"404","error":"Bucket not found","message":"Bucket not found"}
The storage endpoint returns a 400 with a "Bucket not found" message without requiring an API key, while all other endpoints return 401. This inconsistency means:
- An attacker can enumerate public bucket names by brute-forcing the path.
- If a public bucket exists with a guessable name (e.g.,
uploads,images,documents), its contents may be accessible without authentication.
Remediation:
- Ensure no storage buckets are set to public unless absolutely necessary.
- Use signed URLs for file access instead of public buckets.
- Audit bucket names and access policies in the Supabase dashboard.
| Field | Value |
|---|---|
| Severity | Medium–Critical (depending on configuration) |
| Category | OWASP A01:2021 — Broken Access Control |
Common Supabase RLS pitfalls that should be verified:
| # | Pattern | Risk | Check |
|---|---|---|---|
| 1 | RLS not enabled on table | All data readable by anon role |
SELECT relname, relrowsecurity FROM pg_class WHERE relname = 'formas_recebimento'; |
| 2 | Policy uses auth.uid() but table relates via company_id |
Users in same company OK, but no check if user belongs to that company | Verify JWT claims include company_id |
| 3 | INSERT / UPDATE / DELETE policies missing |
RLS only on SELECT — attacker can still write/delete data |
Check policies for all operations |
| 4 | service_role key used in frontend |
Bypasses all RLS entirely | Search frontend bundle for service_role |
| 5 | RLS policy uses current_setting() that can be set by client |
Attacker can inject role claims | Audit policy definitions |
| 6 | Realtime subscriptions ignore RLS | Supabase Realtime respects RLS since 2023, but verify | Check Realtime channel configurations |
Remediation: Run this audit query on your database:
-- Check which tables have RLS enabled
SELECT schemaname, tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY rowsecurity, tablename;
-- Check existing RLS policies
SELECT schemaname, tablename, policyname, permissive, roles, cmd, qual
FROM pg_policies
WHERE schemaname = 'public'
ORDER BY tablename;| Field | Value |
|---|---|
| Severity | Low |
| Category | OWASP A04:2021 — Insecure Design |
Error responses include hints:
{
"message": "No API key found in request",
"hint": "No `apikey` request header or url param was found."
}This tells an attacker:
- The API expects an
apikeyheader or URL parameter. - The parameter name is
apikey. - This is a PostgREST/Supabase deployment.
Remediation: Consider using a reverse proxy that returns generic 401 responses.
| # | Finding | Severity | OWASP Category |
|---|---|---|---|
| 1 | IDOR via client-side company_id filter |
Critical | A01 — Broken Access Control |
| 2 | CORS allows all origins + all methods | High | A05 — Security Misconfiguration |
| 3 | Schema/data model leakage via URL | High | A04 — Insecure Design |
| 4 | Missing security headers | Medium | A05 — Security Misconfiguration |
| 5 | Storage endpoint info disclosure | Medium | A04 — Insecure Design |
| 6 | Potential RLS bypass patterns | Medium–Critical | A01 — Broken Access Control |
| 7 | Verbose error messages | Low | A04 — Insecure Design |
- Enable RLS on ALL public tables — Run
ALTER TABLE <table> ENABLE ROW LEVEL SECURITY;on every table. - Create RLS policies scoped to
auth.jwt()claims (e.g.,company_id). - Verify no
service_rolekey is in frontend code — Search your frontend build forservice_role.
- Audit all RLS policies using the SQL queries above.
- Restrict CORS origins to your frontend domain(s).
- Review storage bucket policies — disable public access where not needed.
- Add write/delete RLS policies — not just
SELECT.
- Add an API gateway or Edge Function proxy to hide direct PostgREST access.
- Implement rate limiting on auth endpoints to prevent credential stuffing.
- Add security headers via reverse proxy or CDN configuration.
- Set up Supabase audit logging to detect unauthorized access attempts.
This audit was performed through external reconnaissance only, without:
- Access to the Supabase dashboard
- Knowledge of the
anonorservice_rolekeys - Authenticated requests to the API
- Access to the application source code
Techniques used:
- HTTP endpoint probing (status code analysis)
- CORS header analysis
- Security header audit
- Error message analysis
- Storage endpoint behavior testing
- URL/query parameter analysis for access control patterns
A full internal audit with dashboard access would reveal significantly more about RLS policies, auth configuration, and storage security.
Report generated on 2026-03-25 — External security assessment