35. Security

The Doctrine library is operating very close to your database and as such needs to handle and make assumptions about SQL injection vulnerabilities.

Doctrine ライブラリはデータベースに非常に近い場所で動作しているため、SQL インジェクションの脆弱性を処理して想定する必要があります。

It is vital that you understand how Doctrine approaches security, because we cannot protect you from SQL injection.

SQL インジェクションからあなたを守ることはできないため、Doctrine がどのようにセキュリティにアプローチするかを理解することが重要です。

Please also read the documentation chapter on Security in Doctrine DBAL. This page only handles Security issues in the ORM.

Doctrine DBAL のセキュリティに関するドキュメントの章もお読みください。このページでは、ORM のセキュリティの問題のみを扱います。

  • DBAL Security Page <https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/security.html>

    DBAL セキュリティ ページ

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.

Doctrine でセキュリティ バグを見つけた場合は、Jira で報告し、セキュリティ レベルを「セキュリティの問題」に変更してください。 Doctrine Coredevelopers とあなただけが見ることができます。

35.1. 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:

ORM は、DBAL 単独よりも SQL インジェクションに対する保護においてはるかに優れています。次の API は、SQL インジェクションから安全であると考えることができます。

  • \Doctrine\ORM\EntityManager#find() and getReference().

    \Doctrine\ORM\EntityManager#find() と getReference()。

  • All values on Objects inserted and updated through Doctrine\ORM\EntityManager#persist()

    Doctrine\ORM\EntityManager#persist() を通じて挿入および更新されたオブジェクトのすべての値

  • All find methods on Doctrine\ORM\EntityRepository.

    Doctrine\ORM\EntityRepository のすべての find メソッド。

  • User Input set to DQL Queries or QueryBuilder methods through
    • setParameter() or variants

      setParameter() またはバリアント

    • setMaxResults()

      setMaxResults()

    • setFirstResult()

      setFirstResult()

  • Queries through the Criteria API on Doctrine\ORM\PersistentCollection and Doctrine\ORM\EntityRepository.

    Doctrine\ORM\PersistentCollection およびDoctrine\ORM\EntityRepository で Criteria API を介してクエリを実行します。

You are NOT safe from SQL injection when using user input with:

ユーザー入力を次のように使用する場合、SQL インジェクションから安全ではありません。

  • Expression API of Doctrine\ORM\QueryBuilder

    Doctrine\ORM\QueryBuilder の Expression API

  • Concatenating user input into DQL SELECT, UPDATE or DELETE statements or Native SQL.

    ユーザー入力を DQL SELECT、UPDATE、または DELETE ステートメントまたはネイティブ 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.

これは、SQL インジェクションは、あらゆる種類のクエリ オブジェクトを操作する場合、Doctrine ORM でのみ発生する可能性があることを意味します。安全なルールは、Query オブジェクトを使用する場合、ユーザー オブジェクトに対して常にプリペアド ステートメント パラメータを使用することです。

Warning

警告

Insecure code follows, don’t copy paste this.

安全でないコードが続きます。これをコピーして貼り付けないでください。

The following example shows insecure DQL usage:

次の例は、安全でない DQL の使用法を示しています。

<?php

// INSECURE
$dql = "SELECT u
          FROM MyProject\Entity\User u
         WHERE u.status = '" .  $_GET['status'] . "'
     ORDER BY " . $_GET['orderField'] . " ASC";

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:

Doctrine では、$dql のどの部分がユーザー入力によるもので、どの部分がそうでないかを知る方法は絶対にありません。独自の解析プロセスがあったとしても、これは技術的に不可能です。正しい方法は次のとおりです。

<?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']);

35.2. 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.

ORM は CRUD アプリケーションにとって非常に便利であり、Doctrine も例外ではありません。ただし、単純に実装された CRUD アプリケーションは、大量割り当てのセキュリティ問題に対して脆弱であることがよくあります。

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:

Doctrine はそのままではこの問題に対して脆弱ではありませんが、 updateFromArray() または updateFromJson() のようなメソッドをエンティティに追加すると、エンティティを一括代入に対して簡単に脆弱にすることができます。脆弱性は次のようになります。

<?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;
        }
    }
}

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:

現在、このエンティティに大量割り当ての可能性が存在し、攻撃者によって悪用されて、次のようにリクエスト データ全体をこのメソッドに渡すと、オブジェクトの「isAdmin」フラグが true に設定される可能性があります。

<?php
$entity = new InsecureEntity();
$entity->fromArray($_POST);

$entityManager->persist($entity);
$entityManager->flush();

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.

この問題を解決するにはどうすればよいですか?一括割り当て関数を介して設定する許可されたキーのホワイトリストが常に必要です。

public function fromArray(array $userInput, $allowedFields = array())
{
    foreach ($userInput as $key => $value) {
        if (in_array($key, $allowedFields)) {
            $this->$key = $value;
        }
    }
}

Table Of Contents

Previous topic

34. The Second Level Cache

34. セカンドレベルキャッシュ

Next topic

Aggregate Fields

集計フィールド

This Page

Fork me on GitHub