SecureStartKit
SecurityFeaturesPricingDocsBlogChangelog
Sign inBuy Now
Home/Security

Security patterns for Next.js, Supabase, and Stripe

10 common weaknesses in a Next.js + Supabase + Stripe SaaS, each with the vulnerable code, the fix, how to spot it in your own codebase, and whether SecureStartKit prevents it by default.

Injection & XSS

  • High · CWE-89
    SQL Injection in Supabase QueriesUser input is interpolated into a .or() or .filter() string, or concatenated into a Postgres function’s dynamic SQL, instead of being passed as a bound value.◐Blast radius limited
  • High · CWE-20
    Unvalidated Server Action InputA Server Action reads FormData fields or typed arguments and passes them directly to a database query, or spreads them with the spread operator, without first running them through a Zod schema.◐Blast radius limited
  • High · CWE-79
    XSS via dangerouslySetInnerHTMLdangerouslySetInnerHTML is set from user-controlled HTML or from Markdown-to-HTML output without first running the string through a sanitizer such as DOMPurify.◐Blast radius limited

Authentication & Access

  • Critical · CWE-285
    Next.js Middleware Auth BypassAuthorization is implemented only in middleware.ts or proxy.ts, with no second check at the data layer or inside the Server Action, route handler, or page component.◐Blast radius limited
  • Medium · CWE-601
    Open Redirect in Auth CallbackAn auth callback or login route redirects to a user-supplied next or redirectTo parameter without checking that it is a same-origin relative path.○Your responsibility

Data Access & RLS

  • High · CWE-639
    IDOR: Missing Ownership CheckA Server Action or route handler reads or writes a record using createAdminClient() with only an id filter and no ownership filter. Because service_role skips Row Level Security, any authenticated user can access any row by supplying an arbitrary id.◐Blast radius limited
  • Critical · CWE-862
    Missing or Disabled RLS PolicyA table holding user data has RLS disabled, or has a policy whose USING expression is not scoped to the current user (for example USING (true)), allowing the anon or authenticated role to read or modify every row.✓Prevented by default

Secrets & Config

  • Critical · CWE-522
    Exposed Supabase service_role KeyThe service_role key appears in browser DevTools under the Network tab or Sources panel, is readable in your built JavaScript bundle, is committed to a git repository, or is returned inside an API response body.◐Blast radius limited

Payments & Webhooks

  • High · CWE-345
    Unverified Stripe Webhook SignatureThe webhook route handler calls req.json() and acts on event.type directly, without calling stripe.webhooks.constructEvent against the raw body and the signing secret in STRIPE_WEBHOOK_SECRET.✓Prevented by default

Abuse & Rate Limiting

  • Medium · CWE-770
    No Rate Limit on Server ActionsA sensitive or expensive Server Action (password reset, magic-link send, AI call, Stripe Checkout creation) runs with no per-IP or per-user rate limit in front of it.◐Blast radius limited