The ARX specification
07 Names and injection
Values
Values reach the database only as parameters (SQL parameters, BSON values, DynamoDB expression values). A value is
never inspected for SQL, operators or expression syntax: text such as Robert'); DROP TABLE x;-- or {"$ne": null} is
stored and matched exactly as given. A document or array given as a match value is compared as data (MongoDB $eq),
never run as an operator.
Names
A name (entity, attribute, alias, link) reaches the database only after it passes the provider's identifier rules (for example ASCII letters, digits and underscores, starting with a letter or underscore, at most the database's length), and then quoted or as an expression placeholder. A name that fails is refused before anything is sent, with one of two codes:
- ARX1017 SuspectedInjection when the name looks like an attempt to change the statement: the injection check below finds something in it.
- ARX1003 InvalidIdentifier otherwise (a space, a leading digit, a name that is too long).
A provider may add markers of its own database's syntax to the check (MongoDB: $; DynamoDB: # and :).
The one way past the identifier rules is an alias declared in the schema catalog (chapter 02): the database name comes only from the catalog, never from a call, and the provider quotes and escapes it by its database's rules.
The injection check
Given a name that already failed the identifier rules, the check reports the first of these it finds, as evidence:
- One of the markers, tested in this order:
'"`;--/**/\()=[]→contains '<marker>'. - A control character other than tab, carriage return and line feed →
contains the control character U+XXXX(four upper-case hexadecimal digits). - When the name contains white space, a word (split at white space) that is one of
select,union,insert,delete,drop,alter,truncate,grant,revoke,exec,execute,sleep,pg_sleep,waitfor, compared ignoring case →contains the SQL word '<WORD>'(the word upper-cased).
Otherwise it reports nothing. Everyday words that are also SQL (and, or, from, set, order, update) aren't in
the list, so "first and last name" stays a plain invalid name. Cases: vectors/injection-check.json.
Echoing a name
A message that repeats a refused name must make it safe first: each control character written as \uXXXX (four
upper-case hexadecimal digits), and the name cut at 64 characters with … appended when it was longer. So a name
can't forge a log line. Cases: vectors/echo.json.
The message of ARX1017 is: <Kind> name '<echoed name>' looks like an SQL injection attempt (<evidence>). ARX refused it before sending anything to the database. Names must come from your code or a fixed list, never from user input.