← Blog

2026-09-02 · 1 min read

Catching IDOR on Next.js + Supabase before Monday

Weekend MVPs leak profiles when RLS is off and /api/users/:id trusts the URL. Here is how to find and fix it.

The Saturday ship

You stood up Next.js, added a users table, and exposed GET /api/users/[id]. You check if (!session) return 401. You ship. On Sunday an attacker changes the UUID in the URL and reads every profile.

That is IDOR — insecure direct object reference. Authentication is not authorization.

Why Supabase makes this easy to miss

Row Level Security is off by default until you write policies. The service role used in Route Handlers bypasses RLS. If your handler does supabase.from("profiles").select("*").eq("id", params.id) without checking that params.id === session.user.id (or an org membership), you have a cross-account read.

Admin routes behind a client-only if (role === "admin") redirect are equally weak. Fetch the JSON anyway.

How AIPentest looks for it

After you authorize the target, agents:

  1. Create or use two test identities when you provide them
  2. Authenticate as user A
  3. Request objects that belong to user B
  4. Flag 200s that return another tenant’s data

Findings include the endpoint, a reproduction sketch, and severity. The first finding is visible on the free scan; the rest live in the unlocked brief.

Fixes that actually work

  • Compare the object owner to the session user on every handler
  • Enable RLS and never use the service role in user-facing paths unless you re-check ownership
  • Prefer opaque IDs plus membership tables over sequential integers
  • Test with two accounts in CI, not just “logged in vs logged out”

Do this before the enterprise questionnaire

Buyers will ask who can read whose data. A 10-minute authorized scan on a URL you own is cheaper than discovering the leak in a security review. Open AIPentest, prove ownership, and queue a scan.