FlawOpen

flawopen.com/SQL Injection in PHP/Doctrine

Is Doctrine ORM safe from SQL injection?

Reference page
Short answer

Yes for DQL (Doctrine Query Language) with bound parameters and the QueryBuilder. Building a DQL or native SQL string with interpolated values reintroduces the same risk as any other ORM's raw-query escape hatch.

UNSAFE
$em->createQuery(
  "SELECT u FROM User u "
  . "WHERE u.id = $userId"
);
SAFE
$qb->select('u')
  ->from('User', 'u')
  ->where('u.id = :id')
  ->setParameter('id', $userId);

The rule

setParameter() binds a value in the DQL query, which Doctrine translates into a bound parameter in the underlying SQL. The value stays separate from query syntax. Concatenating input into a DQL string passed to createQuery() bypasses that protection. The same rule applies to native SQL: Connection::executeQuery() is safe when placeholders and a parameter array are used, but unsafe when input is interpolated into the SQL string.

Bound values do not protect query structure

A placeholder represents a value, not a DQL identifier, SQL column, table name, operator, or sort direction. Doctrine’s ORM security guidance specifically warns that user input passed into QueryBuilder expression methods can still create injection. Keep those expressions in trusted code; map user-selected sort keys and directions through a strict allow-list.

UNSAFE — user input becomes DQL syntax
$qb->orderBy(
  'u.' . $request->query->get('sort'),
  $request->query->get('direction')
);
SAFE — map choices to trusted expressions
$fields = [
    'name' => 'u.name',
    'created' => 'u.createdAt',
];
$directions = ['asc' => 'ASC', 'desc' => 'DESC'];

$key = (string) $request->query->get('sort', 'name');
$sort = $fields[$key] ?? $fields['name'];
$requestedDirection = strtolower(
    (string) $request->query->get('direction', 'asc')
);
$direction = $directions[$requestedDirection] ?? 'ASC';

$qb->orderBy($sort, $direction);

How to check your codebase

rg -n -g '*.php' 'createQuery\(|orderBy\(|addOrderBy\(|executeQuery\(' .
Review each match for string concatenation or interpolation, user input passed to expression APIs, and identifiers that are not selected from a fixed allow-list. Search results are candidates for code review, not proof of exploitability.

References

See also SQL Injection (CWE-89): How It Works and How to Prevent It in Every Language — flawopen.com EF Core FromSqlRaw: Safe Parameterized SQL FastAPI SQL Injection: What Pydantic Does and Doesn’t Stop GORM Raw SQL: Prevent SQL Injection in Go Is Escaping Quotes Enough to Prevent SQL Injection? Is Using an ORM Enough to Prevent SQL Injection? Is Knex.js Safe from SQL Injection?