SurrealQL Injection Prevention
SurrealQL Injection Prevention
Level 8 — Authentication, Permissions & Security Understanding how SurrealDB prevents injection vulnerabilities through query parameterization (
$param), AST-based query parsing, and safe SDK binding practices.
1. Prerequisites
- Parameters (
$param) — Parameter syntax in SurrealQL. - JavaScript / TypeScript SDK — SDK query methods.
2. Term Category
Authentication & Permissions (SurrealQL query injection defense): - Security & Best Practices
3. Explanation
(1) Design Motivation — "Why did we design this?"
In relational SQL databases (PostgreSQL), concatenated string inputs like SELECT * FROM user WHERE email = ' + userInput + ' open up SQL Injection vulnerabilities, allowing attackers to inject malicious clauses like ' OR '1'='1. In MongoDB, unsafe JSON input can trigger NoSQL Injection via operator objects ({ $gt: "" }).
SurrealDB eliminates injection vulnerabilities at the protocol and parser level when developers use parameterized bindings ($param). When parameters are passed separately from the query text, SurrealDB parses the SurrealQL string into a strict Abstract Syntax Tree (AST). Parameter values are treated strictly as data literals and can never mutate the query's structural execution tree.
(2) Reality Metaphor
Think of a bank deposit slip:
- String Concatenation (Dangerous): Handwriting instructions directly on the cash voucher where an attacker can write "Deposit 1,000,000 to Account B".
- Parameterization (Safe): A locked form with pre-printed boxes labeled
AmountandAccount Number. No matter what text a user writes inside theAmountbox (even if they write SQL commands), the bank teller machine treats it strictly as a string value inside that single field.
(3) Code Examples
Short Snippet
// SAFE: Parameterized query binding via SurrealDB JavaScript SDK
const users = await db.query(
'SELECT * FROM user WHERE email = $email AND age >= $minAge',
{ email: userInputEmail, minAge: 18 }
);
Fuller Example
import Surreal from 'surrealdb';
const db = new Surreal();
// Unsafe User Input containing attempted SurrealQL Injection attack
const attackerInput = "tobie' OR role = 'admin' OR name = '";
// 1. DANGEROUS: String Interpolation (Vulnerable to SurrealQL Injection!)
// DO NOT DO THIS!
const unsafeQuery = `SELECT * FROM user WHERE name = '${attackerInput}'`;
// Resulting query text: SELECT * FROM user WHERE name = 'tobie' OR role = 'admin' OR name = ''
// 2. SAFE: Prepared Parameter Binding (Immune to Injection)
const safeResult = await db.query(
'SELECT * FROM user WHERE name = $name',
{ name: attackerInput }
);
// The parser evaluates $name strictly as the literal string "tobie' OR role = 'admin' OR name = '"
4. Common Mistakes & Pitfalls
Mistake 1: Using Template Literals to Build SurrealQL Queries in Node.js
The mistake: Using JavaScript ES6 template literals (`SELECT * FROM user WHERE id = ${id}`) instead of SDK parameter objects.
Why it's wrong: Template string interpolation pastes untrusted input directly into the query string before it reaches the database parser, bypassing all parameter security protections.
Incorrect:
// Vulnerable to injection!
const res = await db.query(`SELECT * FROM post WHERE title = '${req.body.title}'`);
Fix:
// Safe parameter binding
const res = await db.query('SELECT * FROM post WHERE title = $title', {
title: req.body.title
});
Mistake 2: Concatenating User Input Strings into Dynamic SurrealQL Queries
The mistake: Constructing SurrealQL queries with string concatenation `SELECT * FROM user WHERE email = '${input}'`.
Why it's wrong: String concatenation permits SQL injection attacks (e.g. input ' OR 1=1 --). Always use parameterized queries ($email).
Incorrect:
// Vulnerable string concatenation
await db.query(`SELECT * FROM user WHERE email = '${userInput}'`); // ❌ SQL Injection risk!
Fix:
// Safe parameterized query
await db.query('SELECT * FROM user WHERE email = $email', { email: userInput });
Mistake 3: Sanitizing Inputs Manually with Custom Regex Instead of Using Parameterized Bindings
The mistake: Attempting custom string escaping regex functions before string concatenation.
Why it's wrong: Custom escaping functions often have edge-case bypasses. Database parameter binding ($param) guarantees complete protection at the protocol layer.
Incorrect:
const cleanInput = userInput.replace(/'/g, "''"); // ❌ Fragile manual escaping!
Fix:
await db.query('SELECT * FROM user WHERE name = $name', { name: userInput });
5. Practice Exercises
Exercise 1: Parameterized Queries vs String Concatenation
Scenario: Demonstrate rewriting a vulnerable string-concatenated query into a safe parameterized query using SurrealQL parameters.
Requirements:
- Contrast vulnerable string concatenation with parameterized SDK variables.
Answer
Implementation
// ❌ VULNERABLE to SurrealQL Injection:
// const query = `SELECT * FROM user WHERE username = '${userInput}'`;
// ✅ SAFE Parameterized Query:
const result = await db.query(
"SELECT * FROM user WHERE username = $username",
{ username: userInput }
);
Technical Explanation
- Parameterized queries (
$username) send query structure and parameter values separately to the database engine. - Prevents malicious user input from altering SurrealQL syntax trees.
- Eliminates SurrealQL injection vulnerabilities completely.
Exercise 2: Disabling Arbitrary Script Execution CLI Flags
Scenario: Harden a production SurrealDB instance by passing startup flags to disable embedded JavaScript functions.
Requirements:
- Formulate
surreal startcommand passing--deny-scripting.
Answer
Implementation
surreal start > --bind 0.0.0.0:8000 > --user root > --pass "HardenedPass2026!" > --deny-scripting > file:///var/data/surreal.db
Technical Explanation
--deny-scriptingdisables execution of embedded JavaScript functions (function() { ... }).- Mitigates remote code execution risks from arbitrary guest queries.
- Hardens server instance security profiles.
Exercise 3: Input Type Coercion Sanitization
Scenario:
Sanitize user input parameters by enforcing type coercion using <record> or <int> casts.
Requirements:
- Cast
$user_inputto<record<user>>.
Answer
6. Related Terms
- Parameters (
$param) — SurrealQL query variables. - JavaScript / TypeScript SDK — SDK query methods.
PERMISSIONSClause (Table & Field Level) — Row-level authorization.- System Users (
DEFINE USER) — Related concept: System Users (DEFINE USER). - SDK
.query()with Parameters — Related concept: SDK.query()with Parameters.
7. Key Takeaways
- Never concatenate untrusted user input directly into SurrealQL query strings.
- Always use parameterized query bindings (
$param) in SDK.query()calls. - Parameterization guarantees user inputs are treated as literal values, preventing SurrealQL Injection.