See how surrealdb compares to other vendors in security performance
SurrealDB before 3.3.0 contains an authorization bypass in HTTP session construction where checkauth() verifies credentials against Surreal-Auth-NS and Surreal-Auth-DB headers but constructs sessions using Surreal-NS and Surreal-DB headers without validating access permissions. Attackers can authenticate as a user from one tenant while selecting another tenant's namespace and database to read, create, and modify records across tenant boundaries.
SurrealDB versions before 2.2.2 contain a memory exhaustion vulnerability in the string::replace function that fails to restrict resulting string length when using regex patterns. An authenticated attacker can craft a malicious query to exhaust server memory through unbounded string allocations, causing denial of service.
SurrealDB before 2.2.2 fails to validate HTTP redirects in http functions, allowing authenticated users to bypass deny-net restrictions by redirecting to blocked IP addresses. Attackers can host a public server that redirects to denied network targets, enabling server-side request forgery to access internal endpoints and retrieve sensitive information.
SurrealDB before 2.0.5, 2.1.x before 2.1.5, and 2.2.x before 2.2.2 does not enforce a default execution-time limit on embedded JavaScript scripting functions when the scripting capability is explicitly enabled (via --allow-scripting or --allow-all). An authenticated attacker can submit long-running JavaScript functions to exhaust server resources and cause a denial of service. Scripting is disabled by default.
SurrealDB versions before 2.2.2 contain an uncaught exception vulnerability in the net module that allows authenticated users to crash the database. Attackers can send crafted HTTP queries containing null bytes to the /sql endpoint, causing an unhandled exception that crashes the SurrealDB instance and any dependent applications.
SurrealDB before 2.0.5, 2.1.x before 2.1.5, and 2.2.x before 2.2.2 allows authenticated users with OWNER or EDITOR permissions (at the root, namespace, or database level) to define custom database functions via DEFINE FUNCTION using nested FOR loops. Although a single loop's iteration count is constrained, nesting multiple loops (e.g., each with 1,000,000 iterations) is not, so an attacker can execute a function that consumes all server CPU time. Configured timeouts do not stop the execution, rendering the server unresponsive to other queries and connections until it is manually restarted.
SurrealDB before 2.2.6, 2.3.6, and 2.1.8 (and 3.0.0-alpha.7 and earlier) fails to validate DNS-resolved hostnames against --deny-net network access restrictions in its http:: functions. An authenticated user can invoke http::<fn>(<url>) with a hostname that resolves to a denied IP address, causing the server to issue the request anyway and return the response. This bypasses network access controls, allowing access to restricted internal endpoints and potentially retrieving or altering sensitive information and credentials, depending on the deployment.
SurrealDB versions before 2.2.2 contain a local file read vulnerability in the DEFINE ANALYZER statement that allows authenticated users to read arbitrary files on the file system. Attackers with root, namespace, or database level privileges can point analyzers to arbitrary file paths and exfiltrate content from two-column tab-separated files.
SurrealDB before 2.0.5, 2.1.x before 2.1.5, and 2.2.x before 2.2.2 fails to properly escape table and field names in the command-line export command. An authenticated System User with OWNER or EDITOR roles can create tables or fields with malicious names containing SurrealQL. When a higher-privileged user subsequently imports the exported backup, the injected SurrealQL executes, enabling privilege escalation and root-level takeover of the SurrealDB instance. Applications that let users define custom tables or fields are also exposed to a universal second-order SurrealQL injection even when query parameters are sanitized.
SurrealDB before 2.2.2 with scripting enabled fails to properly enforce recursion limits when native functions contain embedded JavaScript that issues new queries. Authenticated attackers can bypass the recursion limit by chaining native and JavaScript function calls to trigger infinite recursion and exhaust server memory.
A PERMISSIONS ... WHERE clause is evaluated with permission enforcement disabled, so it can't recurse into its own checks. But the clause could also contain data-modifying statements, and these ran with enforcement still off — so evaluating a permission check could write to tables the caller cannot write.
For example:
surql DEFINE TABLE post PERMISSIONS FOR update WHERE (CREATE log SET at = time::now()) OR true;
Any user allowed to update a post now also creates a log record, even with no permission on log. The clause is evaluated once per matched record, so one statement can cause several writes.
Impact
Only databases with a PERMISSIONS clause that contains a write are affected; FULL, NONE, and read-only clauses are not.
What an attacker can do:
- With permission to perform the guarded operation (a low-privileged or record user is enough), write to tables in their own database that their permissions would otherwise forbid, by triggering an operation the clause guards. - Cause several writes from a single statement — the clause is evaluated once per matched record. - Trigger unintended events, cascades, or data corruption on those tables.
What it can't do:
- Escape the caller's own namespace and database — a permission clause cannot switch namespace or database. - Perform root- or namespace-level actions such as creating users; the caller's role still applies. - Read hidden data — this is an integrity issue, not disclosure.
Patches
Permission clauses must now be read-only: defining or importing one that contains a write is rejected, and any write attempted while a clause is evaluated is blocked at runtime, including writes reached through a called function. Read-only clauses are unaffected.
- Versions 3.2.0 and later are not affected by this issue.
Workarounds
Users unable to patch should consider the following workarounds:
- Review your PERMISSIONS clauses and remove any containing CREATE, UPDATE, DELETE, RELATE, INSERT, or UPSERT. - Limit who can define schema and vet imported data — such a clause must be defined before it can be triggered.
Resources
- DEFINE TABLE … PERMISSIONS - DEFINE FIELD - fix(core): reject writes in PERMISSIONS clauses and block writes during permission evaluation (included in SurrealDB 3.2.0)
Acknowledgements
Thank you to sondt99 for reporting this issue.
An authenticated user scoped to one namespace/database could invoke a custom API (DEFINE API) belonging to a different namespace/database, reaching another tenant's endpoint.
The route /api/{namespace}/{database}/{endpoint} took the namespace and database from the URL and applied them to the caller's session before the endpoint was looked up or run, without checking that the caller's authenticated scope covered them. Because a custom API handler runs with permissions disabled (definer's rights), the endpoint's own PERMISSIONS clause was the only gate — open to everyone for a PERMISSIONS FULL endpoint. The api::invoke() function was affected the same way, resolving against the session's selected namespace/database (settable via the surreal-ns / surreal-db headers or USE).
Impact
What an attacker can do:
- With valid credentials for any one namespace/database (a VIEWER is enough), invoke a custom API in another namespace/database by naming the victim scope in the URL. - Read data that endpoint returns — including from PERMISSIONS NONE tables, since the handler runs with permissions disabled — or trigger any writes and side effects it performs.
What it can't do:
- Reach a scope without valid credentials for some namespace/database on the instance; this is not an unauthenticated bypass. - Affect single-tenant deployments, or any deployment where callers already hold instance-wide (root) scope.
Patches
The namespace/database is now validated against the caller's authenticated level — which the request cannot change — before the endpoint is resolved or run. A target scope outside that level is rejected with 403 Forbidden, both at the HTTP entry point and at the dispatch step shared with api::invoke(). Root reaches any scope; namespace principals their namespace; database and record principals their exact namespace/database; anonymous callers remain governed by the endpoint's PERMISSIONS.
- Versions 3.2.0 and later are not affected by this issue.
Workarounds
Users unable to patch should consider the following workarounds:
- Disable the custom API HTTP route via capabilities where it is not required. - Treat separate deployments, not namespace/database boundaries, as the tenant isolation boundary on shared instances. - Prefer a PERMISSIONS WHERE clause that checks the authenticated identity over PERMISSIONS FULL (reduces exposure but does not restore the boundary).
Resources
- SurrealQL Documentation — DEFINE API - SurrealDB Documentation — Capabilities - fix(core/api): reject cross-tenant custom API access (included in SurrealDB 3.2.0)
Acknowledgements
SurrealDB thanks sondt99 for reporting this issue.
SurrealDB versions before 1.1.0 fail to enforce recursion depth limits when parsing nested SurrealQL statements including IF, RELATE, and attribute access idioms. Authorized attackers can submit queries with excessive nesting depth to cause stack overflow and crash the server.
SurrealDB before 1.1.1 contains a format string vulnerability in the rquickjs Exception::throwtype function when scripting is enabled. Attackers with scripting privileges can supply format string sequences in error inputs to read arbitrary memory or execute code with SurrealDB process privileges.
SurrealDB versions before 2.0.4 fail to properly enforce field permissions during SELECT, UPDATE, and DELETE operations, allowing authorized users to access unauthorized field values through various query techniques. Attackers can exploit SELECT VALUE operations, field aliasing, function arguments, WHERE clause filtering, RETURN BEFORE clauses, and SET clause references to leak protected field contents despite lacking SELECT permissions.
SurrealDB versions before 1.1.0 fail to properly parse the ID, DB, and NS headers in HTTP REST API requests containing special characters. Unauthenticated attackers can send crafted HTTP requests with malformed header values to trigger an uncaught exception that crashes the server.
SurrealDB versions before 1.1.1 fail to properly validate invocation of custom parameters and functions at root or namespace levels, causing server panic. Authorized clients can invoke these entities at unsupported levels to crash the SurrealDB server, resulting in denial of service.
SurrealDB before 1.0.1 sets default table permissions to FULL instead of NONE, allowing SELECT, CREATE, UPDATE, and DELETE operations on tables without explicit permissions. Attackers with database access or unauthenticated users on publicly exposed instances can perform unrestricted operations on unprotected tables within their authorization scope.
SurrealDB before 2.1.4 silently fails to overwrite table definitions when the DEFINE TABLE ... OVERWRITE clause is used on tables defined with TYPE RELATION. Because table definitions include the PERMISSIONS clause, an attempt to tighten a table's permissions via OVERWRITE does not take effect, and the administrator may incorrectly believe the change was applied. As a result, a client authorized to run queries may continue to access data in that table that the updated (but unapplied) permissions were intended to restrict.
SurrealDB versions before 2.1.0 contain an uncaught exception vulnerability in the rand::time() function that panics when unwrap is called on a None result from timestampopt. Authorized clients can repeatedly invoke rand::time() to reliably trigger server panics and cause denial of service.
SurrealDB versions before 2.1.0 contain a denial of service vulnerability in role conversion that allows privileged owner users to define users with nonexistent roles. Attackers can trigger an uncaught panic by signing in with a user assigned an invalid role, crashing the server.
SurrealDB versions before 2.1.0 contain a denial of service vulnerability in the sorting mechanism when using ORDER BY rand() clause. Authorized clients can execute queries with ORDER BY rand() to trigger a panic in the sorting function, crashing the server.
SurrealDB versions before 2.0.4 contain an uncaught exception handling vulnerability in the parser error rendering code when processing empty strings. Authorized clients can execute malformed queries with empty string conversions to record, duration, or datetime types that cause a panic in error rendering, crashing the server.
SurrealDB before 1.5.5 (and 2.0.0-beta before 2.0.0-beta.3) accepts an arbitrary object in the signin and signup operations of the RPC API without recursively validating it for non-computed values. When a record access method defines a SIGNIN or SIGNUP query and the RPC API is exposed to untrusted users, an unauthenticated attacker can encode a binary object containing a subquery using the bincode serialization format and supply it in place of credentials. The subquery is then executed within the database owner's SIGNIN/SIGNUP query under a system user session with the editor role, allowing the attacker to select, create, update, and delete non-IAM resources (though not view the query results directly, and not affect IAM resources, which require the owner role).
SurrealDB before 1.5.4 fails to properly validate authentication when a scope user switches databases using the USE clause or use method. Attackers with an authenticated session can impersonate an unrelated user in a different database if a user record with an identical identifier exists, allowing unauthorized actions if permissions rely solely on the $auth parameter.
SurrealDB versions before 1.2.1 contain an uncaught exception handling vulnerability in span rendering when parsing queries with errors on line terminator characters. Authorized clients can submit malformed queries that trigger a panic in the span rendering code, crashing the server and causing denial of service.
SurrealDB versions before 1.2.0 contain an uncaught exception vulnerability in the query executor when processing calls to nonexistent built-in functions. Authorized clients can craft pre-parsed queries invoking nonexistent functions to trigger a panic that crashes the server.
SurrealDB before 2.5.0 and before 3.0.0-beta.3 contains a confused deputy privilege escalation vulnerability. Unprivileged users (e.g., those with the database editor role) can create or modify fields containing futures, functions, or closures. Because these are executed in the context of the invoking/querying user rather than their creator, an attacker can plant malicious logic that executes with a higher-privileged user's permissions when that user reads or writes the affected record. This can lead to full privilege escalation, including creation of a root owner and server takeover.
SurrealDB before v2.6.1 (and before v3.0.0-beta.3) contains a denial of service vulnerability in its embedded JavaScript scripting engine, which is enabled via the --allow-scripting capability (disabled by default). Any user able to execute arbitrary queries — including unauthenticated guests when --allow-guests is enabled — can use built-in string functions to construct a large string and pass it to the JavaScript runtime for compilation, triggering a null pointer dereference in the underlying QuickJS-NG engine. This causes the server process to terminate immediately without graceful shutdown, requiring a manual restart. The issue was fixed by updating the rquickjs dependency from v0.9.0 to v0.11.0.
SurrealDB before 3.1.0 silently substitutes the ES384 algorithm when a JWT access method is configured with ALGORITHM ES512 (DEFINE ACCESS ... TYPE JWT ALGORITHM ES512), because the underlying jsonwebtoken crate (v10.x) has no ES512 variant and the mapping defaults to ES384 without any error, warning, or log message. Users who supply the correct P-521 key for ES512 experience authentication handshake failures due to the curve mismatch with ES384 (which expects P-384), and tokens are rejected by external systems expecting genuine ES512 signatures. The flaw cannot be used to forge tokens or compromise data confidentiality or integrity, as ES384 remains cryptographically strong.