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:

  1. One of the markers, tested in this order: ' " ` ; -- /* */ \ ( ) = [ ] → contains '<marker>'.
  2. A control character other than tab, carriage return and line feed → contains the control character U+XXXX (four upper-case hexadecimal digits).
  3. 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.

All chapters of the specification