Enforcing Data Authorization within SurrealDB using Declarative Permissions
Learn how to move authorization from the application layer into SurrealDB using declarative PERMISSIONS to prevent IDOR vulnerabilities and ensure data isolation.
31 Aug 2025, 08:05 UTC

The Problem: Application-Layer Authorization Leakage
In many architectures, the database is treated as a "dumb" store, and the application layer is responsible for filtering data based on user identity. This creates a high risk of Insecure Direct Object Reference (IDOR) vulnerabilities; if a developer forgets a WHERE user_id = $current_user clause in a single API endpoint, the system may leak private data from other users.
The takeaway is to move the authorization boundary from the application code into the database schema. By using SurrealDB's declarative PERMISSIONS clauses, the database itself gates access to records based on the authenticated session, ensuring that unauthorized data never leaves the database engine, regardless of the application's query logic.
Requirements for Database-Level Authorization
- Authenticated Sessions: The database must know who is making the request. This requires record-based authentication where users are stored in a table and sign in to receive a session token.
- Declarative Schema: Tables must be explicitly defined with permissions. Relying on default behaviors is risky as they can change between versions.
- Least Privilege Connection: The application must connect using the user's session token, not a root or database-level administrative credential.
The Smallest Suitable Design
For a standard application, the most efficient design avoids a separate authorization service. Instead, it leverages SurrealDB's built-in identity management.
Components:
- A single SurrealDB instance (Server or Embedded).
- A
usertable that stores credentials and identity. - Business tables (e.g.,
document) withPERMISSIONSpredicates linked to theusertable.
Implementation Example
Assuming SurrealDB 2.x syntax, the following configuration ensures that users can only read and update documents they own.
-- Run these as a root/admin user during schema setup
-- 1. Define the user table
DEFINE TABLE user SCHEMA {
email string,
password string,
}
DEFINE TABLE user PERMISSIONS
FOR select WHERE id = $auth.id;
-- 2. Define the document table with ownership checks
DEFINE TABLE document SCHEMA {
title string,
content string,
owner record,
}
-- Only the owner can select, update, or delete their own documents
DEFINE TABLE document PERMISSIONS
FOR select WHERE owner = $auth.id,
FOR update WHERE owner = $auth.id,
FOR delete WHERE owner = $auth.id,
FOR create WHERE owner = $auth.id;
In this setup, $auth is a special variable provided by SurrealDB that contains the identity of the currently authenticated user. If an application executes SELECT * FROM document; while authenticated as User A, SurrealDB automatically rewrites the query to only return records where owner = User A.
Trust and Data Boundaries
The trust boundary is shifted to the Database Connection. The application acts as a proxy that passes the user's session token to the database.
Critical Boundary Rule: The application must never hold a superuser token during standard request handling. If the application connects as root, the PERMISSIONS clauses are bypassed entirely, and the security model collapses. The application should only use root credentials during migration or administrative tasks.
Operational Checks and Verification
Because permissions are part of the schema, they can drift during migrations. Use the following methods to verify the security posture:
1. Permission Matrix Testing
Do not test permissions using a root account. Create a suite of integration tests that execute as specific record-users:
- Test Case A: User A attempts to
SELECTUser B's record. Expected: Empty result set. - Test Case B: User A attempts to
UPDATEUser B's record. Expected: Error/Permission Denied.
2. Schema Auditing
To verify the live state of a table, run the following command on the database server:
INFO FOR TABLE document;
Capture this output in your CI/CD pipeline and diff it against your version-controlled schema definitions to ensure no unauthorized changes were made to the PERMISSIONS block.
Failure Modes and Limitations
| Failure Mode | Impact | Mitigation |
|---|---|---|
| Complex Predicates | Query performance degradation as predicates are evaluated per record. | Keep WHERE clauses simple; ensure indexed fields are used in predicates. |
| Predicate Logic Bug | Silent data exposure (over-permissive access). | Rigorous unit testing of the grant/deny matrix. |
| Embedded Mode Compromise | Host process access bypasses network security. | Secure the host OS; permissions still apply to the API, but file-level access bypasses them. |
When to Change This Design
This declarative approach is ideal for owner-based access. However, you should move to a more complex application-layer or hybrid model if:
- Complex RBAC: You require highly dynamic Role-Based Access Control (RBAC) that changes based on external factors (e.g., time of day, IP range) not easily captured in a
WHEREclause. - Centralized Auditing: You need a detailed audit log of why a user was granted access, which the database's binary "Allow/Deny" result does not provide.
- Multi-Service Polyglot Access: If multiple services in different languages access the same data, maintaining the schema as the single source of truth is a strength, but if those services require different interpretations of the same data, a middleware layer may be necessary.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.