Database access can expose critical security risks if user input is handled incorrectly. The most important risk is SQL injection.
SQL injection happens when untrusted input is concatenated into SQL text and changes query semantics.
Datazen helps by supporting prepared statements, but it cannot protect you if you build SQL unsafely.
Assume all SQL-fragment APIs are unsafe unless explicitly listed as safe.
The following are safe for user-provided scalar values when used as designed:
Connection#executeQuery(sql, params, types)values inparamsConnection#executeStatement(sql, params, types)values inparamsConnection#insert(table, data, types)values indataConnection#update(table, data, criteria, types)values indataandcriteriaConnection#delete(table, criteria, types)values incriteriaStatement#bindValue()valuesQueryBuilder#setParameter()valuesQueryBuilder#createNamedParameter()valueQueryBuilder#createPositionalParameter()valueQueryBuilder#setFirstResult(offset)numericoffsetQueryBuilder#setMaxResults(limit)numericlimitAbstractPlatform#modifyLimitQuery(sql, limit, offset)numericlimit/offset
Treat all of the following as unsafe for direct user input:
- Raw SQL strings and SQL fragments passed to
Connectionquery methods - Most
QueryBuildermethods that accept SQL fragments (select,from,where,orderBy,groupBy,join,having,set,values, etc.) - Platform SQL expression helpers when you interpolate user input into generated SQL
- Table names, column names, aliases, and sort expressions unless explicitly validated/allow-listed by application code
Never concatenate untrusted input into SQL.
// Wrong
const sql = "SELECT * FROM users WHERE username = '" + username + "'";
await conn.executeQuery(sql);Use placeholders and bind values.
Connection helper:
const result = await conn.executeQuery(
"SELECT * FROM users WHERE username = ?",
[username],
);Named parameters:
const result = await conn.executeQuery(
"SELECT * FROM users WHERE username = :user",
{ user: username },
);Statement API:
const stmt = await conn.prepare("SELECT * FROM users WHERE username = :user");
stmt.bindValue("user", username);
const result = await stmt.executeQuery();QueryBuilder:
const qb = conn
.createQueryBuilder()
.select("id", "username")
.from("users")
.where("username = :user")
.setParameter("user", username);
const rows = await qb.fetchAllAssociative();Pass explicit types when needed:
ParameterType.*for low-level binding semantics- Datazen type names (
"datetime","json", etc.) for conversion layer ArrayParameterType.*for list expansion (IN (...)style scenarios)
Using explicit types improves correctness and reduces driver-specific coercion surprises.
Connection#quote() exists, but prepared statements are strongly preferred.
Manual quoting is easier to misuse and harder to review.
// Technically valid, but discouraged
const sql = "SELECT * FROM users WHERE username = " + (await conn.quote(username));
const result = await conn.executeQuery(sql);For SQL literals generated by platform utilities, use
AbstractPlatform#quoteStringLiteral().
- Never concatenate untrusted input into SQL text.
- Bind every user value as a parameter.
- Do not pass user input as identifiers (table/column/order expressions) without strict allow-listing.
- Prefer explicit parameter types for non-trivial values.
- Keep
setFirstResult/setMaxResultsinputs numeric.
In insert/update/delete, only values are parameterized. Table names and
column keys are SQL fragments and must not come from untrusted input without
strict allow-listing.