• Agents
  • Pricing
  • Blog
Log in
Get started

Security for apps built with AI. Paste a URL, get a report, fix what matters.

Product

  • How it works
  • What we find
  • Pricing
  • Agents
  • MCP Server
  • CLI
  • GitHub Action

Resources

  • Guides
  • Blog
  • Docs
  • OWASP Top 10
  • Glossary
  • FAQ

Security

  • Supabase Security
  • Next.js Security
  • Lovable Security
  • Cursor Security
  • Bolt Security

Legal

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • Imprint
© 2026 Flowpatrol. All rights reserved.
Home/OWASP Top 10/Web Top 10/A01: Broken Access Control
A01CWE-284CWE-285CWE-639

Change the ID, see what happens
Broken Access Control

The bug where one user can read another user's data just by editing a number in the URL.

Affects 3.81% of tested apps — the highest of any category.

Reference: Web Top 10 (2021) — A01·Last updated April 7, 2026·By Flowpatrol Team
Broken Access Control illustration

Most AI-generated APIs aren't broken because the code is wrong. They're broken because the code is missing a single line — the one that asks 'is this actually your data?' The route works perfectly for the user who built the app. Then a second user shows up.

Access control decides who is allowed to do what. Broken access control means the rules are missing or wrong — usually missing entirely on a route that 'works' because the only person who tested it was the owner. Every other user gets the same answer the owner did.

What your AI actually built

You asked for a CRUD API for invoices, orders, profiles, projects — pick one. The model gave you exactly that. A clean route that fetches a record by id and returns it as JSON. Tested it, worked, shipped it.

What it didn't give you was the ownership check. The route happily returns the row whether the requester owns it or not, because nothing in the prompt said 'and only let the right user read it.' That's not a code bug. That's a missing requirement the model couldn't infer.

On Supabase apps, the same pattern shows up as a missing Row Level Security policy. The table is created, the policy slot is empty, and every authenticated user can read every row. Same bug, different layer.

How it gets exploited

Two accounts. The attacker signs up like a normal user, then opens the network tab.

1
Watch one request
They log in, click their own invoice, and notice the URL: /api/invoices/417.
  • 2
    Change one number
    They edit 417 to 416. The server returns somebody else's invoice — full JSON, no error.
  • 3
    Walk the range
    A 20-line script enumerates 1 through 5000. Every record comes back. Every customer.
  • 4
    Pivot
    The same pattern works on /api/users, /api/orders, /api/uploads. The bug is structural, not local.
  • The attacker now has the entire user database, every invoice, and every uploaded file — without a single password, exploit, or unusual request. The logs show only authenticated traffic.

    Vulnerable vs Fixed

    Vulnerable — no ownership check
    // app/api/invoices/[id]/route.ts
    export async function GET(req, { params }) {
      const invoice = await db.invoice.findUnique({
        where: { id: params.id },
      });
    
      // Whoever asks gets the row.
      return Response.json(invoice);
    }
    Fixed — scoped to the session
    // app/api/invoices/[id]/route.ts
    export async function GET(req, { params }) {
      const session = await getSession(req);
    
      const invoice = await db.invoice.findUnique({
        where: {
          id: params.id,
          userId: session.user.id, // ownership lives in the query
        },
      });
    
      if (!invoice) {
        return Response.json({ error: 'Not found' }, { status: 404 });
      }
    
      return Response.json(invoice);
    }

    One extra clause in the where. That is the entire fix. The 404 instead of 403 is intentional — never confirm whether a record exists when the user is not allowed to see it.

    A real case

    170 Lovable apps leaked their entire users tables

    Researchers walked into 170 production Supabase apps via the anon key because RLS was never enabled — the same Broken Access Control pattern, one layer down.

    Read the case study

    Related reading

    Glossary

    Insecure Direct Object Reference (IDOR)Broken Object Level Authorization (BOLA)RLS in Supabase & PostgreSQL (Row Level Security)Missing Admin Checks (Broken Function Level Authorization)

    What we find

    broken access control

    From the blog

    supabase rls the security feature your ai forgotlovable rls vulnerability 170 apps exposed

    References

    • A01: Broken Access Control — official OWASP entry
    • OWASP Top 10 for Web Applications (2021) — full list
    • CWE-284 on cwe.mitre.org
    • CWE-285 on cwe.mitre.org
    • CWE-639 on cwe.mitre.org

    Find every Broken Access Control bug in your app.

    Flowpatrol logs in as multiple users and cross-tests every endpoint. Five minutes. One URL.

    Try it free