You are browsing a version that is no longer maintained. |
Security
The Doctrine library is operating very close to your database and as such needs to handle and make assumptions about SQL injection vulnerabilities.
It is vital that you understand how Doctrine approaches security, because we cannot protect you from SQL injection.
Please also read the documentation chapter on Security in Doctrine DBAL. This page only handles Security issues in the ORM.
DBAL Security Page <https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/security.html>
If you find a Security bug in Doctrine, please report it on Jira and change the Security Level to "Security Issues". It will be visible to Doctrine Core developers and you only.
User input and Doctrine ORM
The ORM is much better at protecting against SQL injection than the DBAL alone. You can consider the following APIs to be safe from SQL injection:
\Doctrine\ORM\EntityManager#find()
andgetReference()
.- All values on Objects inserted and updated through
Doctrine\ORM\EntityManager#persist()
- All find methods on
Doctrine\ORM\EntityRepository
. - User Input set to DQL Queries or QueryBuilder methods through
-
setParameter()
or variantssetMaxResults()
setFirstResult()
- Queries through the Criteria API on
Doctrine\ORM\PersistentCollection
andDoctrine\ORM\EntityRepository
.
You are NOT safe from SQL injection when using user input with:
- Expression API of
Doctrine\ORM\QueryBuilder
- Concatenating user input into DQL SELECT, UPDATE or DELETE statements or Native SQL.
This means SQL injections can only occur with Doctrine ORM when working with Query Objects of any kind. The safe rule is to always use prepared statement parameters for user objects when using a Query object.
Insecure code follows, don't copy paste this. |
The following example shows insecure DQL usage:
For Doctrine there is absolutely no way to find out which parts of $dql
are
from user input and which are not, even if we have our own parsing process
this is technically impossible. The correct way is:
1 <?php
$orderFieldWhitelist = array('email', 'username');
$orderField = "email";
if (in_array($_GET['orderField'], $orderFieldWhitelist)) {
$orderField = $_GET['orderField'];
}
$dql = "SELECT u
FROM MyProject\Entity\User u
WHERE u.status = ?1
ORDER BY u." . $orderField . " ASC";
$query = $entityManager->createQuery($dql);
$query->setParameter(1, $_GET['status']);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Preventing Mass Assignment Vulnerabilities
ORMs are very convenient for CRUD applications and Doctrine is no exception. However CRUD apps are often vulnerable to mass assignment security problems when implemented naively.
Doctrine is not vulnerable to this problem out of the box, but you can easily
make your entities vulnerable to mass assignment when you add methods of
the kind updateFromArray()
or updateFromJson()
to them. A vulnerable
entity might look like this:
1 <?php
#[Entity]
class InsecureEntity
{
#[Id, Column, GeneratedValue]
private int|null $id = null;
#[Column]
private string $email;
#[Column]
private bool $isAdmin;
/** @param array<string, mixed> $userInput */
public function fromArray(array $userInput): void
{
foreach ($userInput as $key => $value) {
$this->$key = $value;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Now the possiblity of mass-assignment exists on this entity and can be exploited by attackers to set the "isAdmin" flag to true on any object when you pass the whole request data to this method like:
You can spot this problem in this very simple example easily. However in combination with frameworks and form libraries it might not be so obvious when this issue arises. Be careful to avoid this kind of mistake.
How to fix this problem? You should always have a whitelist of allowed key to set via mass assignment functions.