Context
- I have a query filtered by the
current_user object, using custom properties defined in the metadata (used to restrict which rows a user can access)
-
The "Prevent query variable spoofing" option is enabled.
-
Expected behavior
When logging in as a user with no rights, the query correctly returns no rows.
This works fine from within the app.
Vulnerability found
From the browser console, a user can:
-
Use "Copy as cURL" on the query's network request
-
Replay that request from a terminal, modifying the variables/parameters sent
-
Bypass the current_user-based filtering entirely and retrieve rows they shouldn't have access to
In other words, "Prevent query variable spoofing" appears to only protect against tampering from within the UI, but the underlying HTTP request still accepts arbitrary values server-side.
Plan constraint
I'm on the Business plan, not Enterprise. From what I understand, Access policies (server-side row-level security enforced on the resource itself) are Enterprise-only and limited to PostgreSQL resources. Can anyone confirm this is still accurate, and whether there's any workaround on Business (e.g., a parameterized backend proxy query, hardcoded SQL views with filtering, or anything else) to properly secure this without upgrading?
Questions
-
Is this the expected/documented behavior of "Prevent query variable spoofing" (client-side protection only)?
-
Is there any server-side enforcement path available outside of Access policies on Business?
-
Is Postgres RLS (or an equivalent database-level mechanism) really the only reliable fix here, independent of the Retool plan?
Any documentation or real-world feedback on best practice would be greatly appreciated, this is currently blocking in production.
Summary
A query filters rows by current_user custom metadata with "Prevent query variable spoofing" enabled, yet using the browser's "Copy as cURL" and replaying the request with modified variables bypasses the current_user-based row filtering. The question is whether this is expected, and what server-side row-level enforcement is available on a Business (Cloud) plan short of Enterprise Access Policies.
AI Response
This is largely expected given how the feature is scoped. "Prevent query variable spoofing" is a server-side check that rejects requests whose current_user properties (e.g. current_user.email) don't match the logged-in user's expected values — but per the docs, it does NOT apply when the expression contains additional/derived logic (e.g. mapping over current_user.groups or transforming metadata), which is a likely reason a replayed request slips through. To confirm coverage, ensure the filter references a direct current_user property rather than computed logic, and verify the "Prevent query variable spoofing" toggle is enabled under Settings > Beta. For enforcement that holds regardless of the request payload and independent of Retool plan, push the restriction down to the database itself via database-level row-level security (e.g. Postgres RLS or a filtered view/parameterized proxy); resource-side Access Policies are indeed Enterprise-only and Postgres-only, so DB-level RLS is the reliable cross-plan path.
Sources
Row-level security | Retool Docs
Documents how current_user-based row-level filtering and query variable spoofing prevention work, and contrasts query-level filtering (any plan/resource) with Enterprise/Postgres-only Access Policies — plus recommends pushing filtering to the database itself.
How to prevent end-users from tampering with query parameters in embedded Retool apps (multi-tenant context)?
A solved, security-tagged thread describing the same userParams tampering/replay scenario, where the accepted guidance is to rely on the current_user object protected by the Retool backend (anti-spoofing) and to use row-level security, noting external/non-Retool auth can't be server-side validated.
The Community Team is testing out a new automation. Let us know if it's helpful (or not) by leaving a
,
, or
. Or by marking this post as the "Solution"! Let us know if you have any feedback here. 
1 Like