glossary
Missing WITH CHECK Policy
Missing WITH CHECK clauses expose write paths even when reads are locked down. This page explains it in plain English, then goes deeper into how it works in Supabase/Postgres, what commonly goes wrong, and how to fix it without relying on fragile client-side rules.
What “Missing WITH CHECK Policy” means (plain English)
WITH CHECK ensures inserts and updates obey the same ownership policy as SELECT. Missing WITH CHECK Policy is a practical security issue for teams using Supabase because exposed tables, storage, or RPC endpoints can return or mutate data beyond intended account boundaries.
How Missing WITH CHECK Policy works in Supabase/Postgres (technical)
Without WITH CHECK, users can insert rows that violate your intended invariants, so client writes become a loophole even if reads are protected.
Attack paths & failure modes for Missing WITH CHECK Policy
- Missing WITH CHECK Policy: direct API bypass: The team focused on SELECT policies while writes still happen directly from the client.
- Missing WITH CHECK Policy: migration drift regression: A new table ships with SELECT protections but the write policy is incomplete.
- Missing WITH CHECK Policy: direct API bypass: Users could insert or update rows with other users’ IDs and escalate privileges.
- Missing WITH CHECK Policy: migration drift regression: Without ownership checks on writes, attackers could insert or modify rows belonging to others.
- The configuration doesn’t match what the UI implies (direct API access bypasses the app).
- Policies/grants drift over time and widen access without anyone noticing.
- Fixes are applied without verification, leading to false confidence.
Why Missing WITH CHECK Policy matters for Supabase security
Attackers can impersonate other users or escalate privileges via unchecked writes. If Missing WITH CHECK Policy remains unresolved, attackers can automate enumeration and unauthorized writes at API speed. Treat it as a production reliability risk as well as a data security risk, because incidents spread quickly once clients discover weak access boundaries.
Common Missing WITH CHECK Policy mistakes that lead to leaks
- Adding only a SELECT policy and skipping WITH CHECK.
- Assuming existing grants handle write restrictions.
- Neglecting write exposure when auditing policies.
- Missing WITH CHECK Policy: direct API bypass: Users could insert or update rows with other users’ IDs and escalate privileges.
- Missing WITH CHECK Policy: migration drift regression: Without ownership checks on writes, attackers could insert or modify rows belonging to others.
Where to look for Missing WITH CHECK Policy in Supabase
- Your grants, policies, and any direct client access paths.
- Storage and RPC settings (common blind spots).
How to detect Missing WITH CHECK Policy issues (signals + checks)
Use this as a quick checklist to validate your current state:
- Try the same queries your frontend can run (anon/authenticated). If sensitive rows come back, you have exposure.
- Verify RLS is enabled and (for sensitive tables) forced.
- List policies and look for conditions that don’t bind rows to a user or tenant.
- Audit grants to
anon/authenticatedon sensitive tables and functions. - Missing WITH CHECK Policy: direct API bypass: Write paths can be riskier than reads.
- Missing WITH CHECK Policy: direct API bypass: WITH CHECK must match the read logic.
- Missing WITH CHECK Policy: direct API bypass: Backend endpoints enable validation and auditing.
- Re-test after every migration that touches security-critical tables or functions.
How to fix Missing WITH CHECK Policy (backend-only + zero-policy posture)
Mockly’s safest default is backend-only access: the browser should not query tables, call RPC, or access Storage directly.
- Decide which operations must remain client-side (often: none for sensitive resources).
- Create server endpoints (API routes or server actions) for required reads/writes.
- Apply hardening SQL: enable+force RLS where relevant, remove broad policies, and revoke grants from client roles.
- Generate signed URLs for private Storage downloads on the server only.
- Re-run a scan and confirm the issue disappears.
- Add a regression check to your release process so drift doesn’t reintroduce exposure. Fixes that worked in linked incidents:
- Missing WITH CHECK Policy: direct API bypass: Move writes behind backend endpoints that enforce ownership and mirror the read policy with WITH CHECK.
- Missing WITH CHECK Policy: migration drift regression: Add WITH CHECK clauses, route writes through backend endpoints, and include policy verification steps in migrations.
Verification checklist for Missing WITH CHECK Policy
- Attempt direct access using client credentials and confirm it fails.
- Apply a backend-only fix pattern and verify end-to-end behavior.
- Re-run a scan after changes and after the next migration.
- Missing WITH CHECK Policy: direct API bypass: Write paths can be riskier than reads.
- Missing WITH CHECK Policy: direct API bypass: WITH CHECK must match the read logic.
- Missing WITH CHECK Policy: direct API bypass: Backend endpoints enable validation and auditing.
- Missing WITH CHECK Policy: direct API bypass: Test both read and write exposure, not just SELECT.
- Missing WITH CHECK Policy: migration drift regression: Migrations easily forget write clauses.
SQL sanity checks for Missing WITH CHECK Policy (optional, but high signal)
If you prefer evidence over intuition, run a small set of SQL checks after each fix.
The goal is not to memorize catalog tables — it’s to make sure the access boundary you intended is the one Postgres actually enforces:
- Confirm RLS is enabled (and forced where appropriate) for tables tied to this term.
- List policies and read them as plain language: who can do what, under what condition?
- Audit grants for anon/authenticated and PUBLIC on the tables, views, and functions involved.
- If Storage is involved: review bucket privacy and policies for listing/reads.
- If RPC is involved: review EXECUTE grants for functions and whether privileged functions are server-only.
Pair these checks with a direct API access test using client credentials. When both agree, you can ship the fix with confidence.
Over time, keep a small “query pack” for the checks you trust and run it after every migration. That’s how you prevent quiet regressions.
Prevent Missing WITH CHECK Policy drift (so it doesn’t come back)
- Add a repeatable checklist and re-run it after schema changes.
- Prefer backend-only access for sensitive resources.
- Keep one reusable verification test for “Missing WITH CHECK Policy: direct API bypass” and rerun it after every migration that touches this surface.
- Keep one reusable verification test for “Missing WITH CHECK Policy: migration drift regression” and rerun it after every migration that touches this surface.
Rollout plan for Missing WITH CHECK Policy fixes (without breaking production)
Many hardening changes fail because teams revoke direct access first and only later discover missing backend paths.
Use this sequence to reduce both risk and outage pressure:
- Implement and verify the backend endpoint or server action before permission changes.
- Switch clients to that backend path behind a feature flag when possible.
- Then revoke direct client access (broad grants, permissive policies, public bucket reads, or broad EXECUTE).
- Run direct-access denial tests and confirm authorized backend flows still succeed.
- Re-scan after deployment and again after the next migration.
This turns security fixes into repeatable rollout mechanics instead of one-off emergency changes.
Incident breakdowns for Missing WITH CHECK Policy (real scenarios)
Missing WITH CHECK Policy: direct API bypass
Scenario: The team focused on SELECT policies while writes still happen directly from the client.
What failed: Users could insert or update rows with other users’ IDs and escalate privileges.
What fixed it: Move writes behind backend endpoints that enforce ownership and mirror the read policy with WITH CHECK.
Why the fix worked: Backend writes let you validate invariants and log changes before they hit the database.
Key takeaways:
- Write paths can be riskier than reads.
- WITH CHECK must match the read logic.
- Backend endpoints enable validation and auditing.
- Test both read and write exposure, not just SELECT.
Read full example: Missing WITH CHECK Policy: direct API bypass
Missing WITH CHECK Policy: migration drift regression
Scenario: A new table ships with SELECT protections but the write policy is incomplete.
What failed: Without ownership checks on writes, attackers could insert or modify rows belonging to others.
What fixed it: Add WITH CHECK clauses, route writes through backend endpoints, and include policy verification steps in migrations.
Why the fix worked: Ensuring writes obey the same constraints prevents new tables from opening up exposures.
Key takeaways:
- Migrations easily forget write clauses.
- CI should validate that policies cover INSERT/UPDATE.
- Document the difference between SELECT and WRITE guards.
- Validate write exposure with direct API tests.
Read full example: Missing WITH CHECK Policy: migration drift regression
Real-world examples of Missing WITH CHECK Policy (and why they work)
- Missing WITH CHECK Policy: direct API bypass — Writes bypassed ownership checks because WITH CHECK was missing.
- Missing WITH CHECK Policy: migration drift regression — A migration introduced a table where writes lacked WITH CHECK, reopening a loophole.
Related terms
- Tenant ID Trusted from Client →
/glossary/tenant-id-trust-in-client - Broad SELECT for Authenticated Role →
/glossary/broad-authenticated-select
FAQ
Is Missing WITH CHECK Policy enough to secure my Supabase app?
It’s necessary, but not sufficient. You also need correct grants, secure Storage/RPC settings, and a backend-only access model for sensitive operations.
What’s the quickest way to reduce risk with Missing WITH CHECK Policy?
Remove direct client access to sensitive resources, enable/force RLS where appropriate, and verify via a repeatable checklist that anon/authenticated cannot query what they shouldn’t.
How do I verify the fix is real (not just a UI change)?
Attempt direct API queries using the same client credentials your app ships. If the database denies access (401/403) and your backend endpoints still work, your fix is effective.
Next step
Want a quick exposure report for your own project? Run a scan in Mockly to find public tables, storage buckets, and RPC functions — then apply fixes with verification steps.
Explore related pages
cross
Remove over-permissive RLS policies (adopt deny-by-default)/templates/access-control/remove-over-permissive-policies