Skip to content

SQL Injection Protection

SQL injection occurs when an external value is concatenated into the query string before reaching the database. Using sql"...", Zolo never concatenates: every interpolation {expr} becomes a positional ? and the value is sent separately by the driver. The database receives the query structure on one channel and the data on another — SQL metacharacters in the value are treated as text, not as code.

The classic payload '; DROP TABLE users; -- is inserted as a variable in a sql"..." query. The table remains intact and no rows are found.

08-injection-safe.zolo
// Feature: `sql"..."` interpolations are *bound parameters*, not concatenated

// Syntax: `sql"SELECT ... WHERE x = {evil}"` — at compile time the

// `{evil}` becomes `?` and the value is sent as a parameter. SQL

// metacharacters in the value are literal, not parsed.

// When to use: ALWAYS. Never build queries with `+` or interpolation

// outside `sql"..."`. This file demonstrates why.


use std::Database

let db = Database.open("sqlite://:memory:")?
defer db.close()

db.execute(sql"CREATE TABLE users (name TEXT)")?
db.execute(sql"INSERT INTO users VALUES ('alice')")?
db.execute(sql"INSERT INTO users VALUES ('bob')")?

// The classic injection payload — in a string-concat builder it

// would terminate the query, drop the table, and comment out the rest.

// Inside `sql"..."` it's just a string parameter that doesn't match.

let evil = "'; DROP TABLE users; --"
let rows = sql"SELECT name FROM users WHERE name = {evil}".query(db)?
var count = 0
for _ in rows { count = count + 1 }
print("matched: {count}")

// expected: matched: 0


// Proof: the table is still there, both rows intact.

let total = sql"SELECT COUNT(*) FROM users".scalar(db)?
print("total: {total}")

// expected: total: 2


db.query("SELECT * FROM users") ?> .each(|u| print(u.name))

Requires the Zolo CLI/host — open in the playground or run locally.

The practical rule is simple:

  • Use sql"..." (or ? + bindings) always when a value comes from outside the source code.
  • Never build queries with + or with interpolation outside the sql"..." literal.

Challenge

Try building the same query without sql"..." using string concatenation and observe what happens to the table. What error message does Zolo emit?

enespt-br