SurrealDB Insecure Default Table Permissions
CVE-2023-54366: SurrealDB defaulted table permissions to FULL instead of NONE, exposing all tables to unrestricted read/write operations by any authenticated or unauthenticated user.
Overview
CVE-2023-54366 is a critical authorization misconfiguration in SurrealDB, the multi-model NewSQL database, affecting all releases prior to version 1.0.1. The vulnerability stems from a single architectural decision: when a table is created without an explicit PERMISSIONS clause, the database engine defaults the permission set to FULL — granting SELECT, CREATE, UPDATE, and DELETE to any authenticated scope user, and in publicly exposed configurations, potentially to unauthenticated principals as well. This inverts the principle of least privilege at the database engine level, making the insecure path the default path.
The flaw is particularly dangerous given SurrealDB’s positioning as a backend-as-a-service-friendly database. Many deployments expose SurrealDB’s HTTP or WebSocket interface directly to client applications, relying on its record-level permission system as the primary access control boundary. When that permission system silently defaults to open, the entire authorization model collapses without any visible error or warning to the developer.
Security researchers identified the issue during review of SurrealDB’s permission inheritance logic prior to the 1.0.1 stable release. Any organization that stood up a SurrealDB instance before upgrading to 1.0.1 — particularly those using the surrealdb.js, surrealdb.py, or other client SDKs with scope-based authentication — should treat all tables created without explicit permissions as fully compromised and audit their data accordingly.
Technical Analysis
SurrealDB’s permission model is evaluated at query time against a table’s stored definition. Each table definition carries a PERMISSIONS block that independently controls the four DML operations. In vulnerable versions, the DefineTableStatement struct initializes its permission fields using a Permission::default() implementation that resolves to Permission::Full rather than Permission::None.
The root cause lives in the table definition logic. When a developer issues:
DEFINE TABLE user;
The database materializes a table definition equivalent to:
-- What the engine silently produces in versions < 1.0.1
DEFINE TABLE user
PERMISSIONS
FOR select FULL
FOR create FULL
FOR update FULL
FOR delete FULL;
In Rust, the underlying default implementation for the permission enum looked approximately like this in the vulnerable codebase:
// VULNERABLE: src/sql/permission.rs (pre-1.0.1)
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Permission {
None,
Full,
Specific(Value),
}
impl Default for Permission {
fn default() -> Self {
// Insecure default: grants unrestricted access when no
// permission clause is explicitly provided.
Permission::Full
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Permissions {
pub select: Permission,
pub create: Permission,
pub update: Permission,
pub delete: Permission,
}
Because Permissions derives Default and each field type’s Default implementation returns Permission::Full, any DEFINE TABLE statement that omits the PERMISSIONS clause produces a fully open table definition. The engine never warns the operator, never logs the open grant, and never requires an explicit acknowledgment that the table is world-accessible within its authentication scope.
This matters most in SurrealDB’s scope-based authentication model. A scope user — an end-user authenticated via a DEFINE SCOPE / SIGNIN flow — is intentionally given direct query access to the database. The permission system on individual tables is the only mechanism preventing that authenticated end-user from querying tables they have no business accessing. When every table defaults to FULL, there is no isolation between tables at the scope-user level.
Impact
An attacker or unauthorized authenticated scope user can perform unrestricted SELECT, CREATE, UPDATE, and DELETE operations on any table defined without an explicit permission clause. In a typical SurrealDB-backed web application, this translates to:
- Full data exfiltration: Any scope user can
SELECT * FROMsensitive tables including those storing other users’ PII, financial records, or session tokens. - Data tampering:
UPDATEaccess enables an attacker to modify records they do not own — changing roles, elevating privileges stored in application-layer columns, or corrupting business-critical data. - Data destruction:
DELETEaccess allows bulk or targeted record deletion without authentication beyond a valid scope token. - Unauthorized record injection:
CREATEaccess enables an attacker to insert arbitrary records, which can poison application logic that trusts database contents.
In publicly exposed SurrealDB instances running with permissive network access controls, the risk extends to unauthenticated users if the guest access configuration is not explicitly locked down. The CVSS 8.8 HIGH score reflects a network-exploitable, low-complexity attack with no required privileges and high impact across confidentiality, integrity, and availability (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H).
How to Fix It
Upgrade immediately. The fix is available in SurrealDB 1.0.1 and all later releases. The corrected Default implementation for Permission returns Permission::None, meaning any table defined without an explicit permissions clause is locked down by default.
# Cargo-based projects: update Cargo.toml
surrealdb = ">=1.0.1"
cargo update surrealdb
# Docker
docker pull surrealdb/surrealdb:latest # pulls ≥ 1.0.1
docker pull surrealdb/surrealdb:v1.0.1 # pin to patched release
After upgrading, audit all existing table definitions and add explicit permission clauses that reflect your actual access requirements. Do not assume tables are locked down simply because no permissions were specified before the upgrade — tables created by older versions may have persisted FULL grants in their stored definitions.
-- FIXED: Explicit least-privilege permissions for a user-facing table
DEFINE TABLE post
PERMISSIONS
FOR select WHERE published = true OR user = $auth.id
FOR create WHERE $auth.id != NONE
FOR update WHERE user = $auth.id
FOR delete WHERE user = $auth.id;
-- For purely internal/server-side tables that scope users should never touch:
DEFINE TABLE audit_log
PERMISSIONS NONE;
The corrected Rust default in 1.0.1 is:
// FIXED: src/sql/permission.rs (1.0.1+)
impl Default for Permission {
fn default() -> Self {
// Secure default: deny all access unless explicitly granted.
Permission::None
}
}
Treat any DEFINE TABLE statement without a PERMISSIONS clause as a code review finding in your development workflow. Enforce this at the schema migration level, not as an afterthought.
Our Take
Insecure-by-default configurations are one of the most consistently underestimated vulnerability classes in enterprise software. They produce no observable error, pass functional tests, and remain invisible until an attacker — or a penetration tester — deliberately looks for them. CVE-2023-54366 is a textbook example: a single enum variant in a Default implementation silently dismantles an entire authorization architecture.
The pattern is worryingly common in emerging database and BaaS technologies where developer ergonomics are prioritized during rapid early development. “It works out of the box” is a compelling feature until “out of the box” means “completely open.” Mature security engineering demands that the default configuration be the secure configuration, and that developers must explicitly opt in to reduced security rather than opt in to security itself.
For enterprises evaluating or running SurrealDB in production, this vulnerability underscores the need to treat database permission models as first-class security artifacts — reviewed in code, tested in CI, and audited periodically. An application that looks secure at the API layer may be entirely exposed at the database layer if permission configuration is left to implicit defaults.
Detection with SAST
Static analysis for this vulnerability class targets missing or overly permissive resource permission declarations in database schema definitions. Offensive360’s SAST engine flags the following patterns in SurrealDB schema files and inline query strings:
- CWE-276 (Incorrect Default Permissions): Any
DEFINE TABLEstatement that omits thePERMISSIONSclause entirely is flagged as a potential insecure default. In codebases running SurrealDB < 1.0.1, this is a confirmed HIGH-severity finding. - CWE-732 (Incorrect Permission Assignment for Critical Resource):
DEFINE TABLEstatements containingPERMISSIONS ... FULLon tables that store user data, credentials, or financial records trigger a policy violation under least-privilege rules. - Taint analysis on query construction: Dynamic
DEFINE TABLEstrings built in application code are traced for missing permission clauses before they reach the SurrealDB client SDK’squery()call. - Dependency version checks: Projects declaring
surrealdbas a Cargo or npm dependency with a version constraint that satisfies< 1.0.1are flagged at the dependency manifest level, regardless of whether schema files are available for analysis.
Rule category: Insecure Configuration / Database Authorization. This class of finding is most effectively caught during code review and CI gating — well before a staging environment exposes it to real user traffic.
References
Detect this vulnerability class in your codebase
Offensive360 SAST scans your source code for CVE-2023-54366-class vulnerabilities and thousands of other patterns — across 60+ languages.