如何防止在 Symfony 中延迟加载易受攻击的实体

How to prevent lazy loading of vulnarable entities in Symfony

我的问题比较简单,但是在网上google了一个小时也没有找到任何线索。

我正在尝试构建一个 Symfony API,但是当返回 json 输出时,它会延迟加载每个关系到输出中。虽然这没什么大不了的(在大多数情况下),但当它对用户信息进行这种欺骗时,它真的很糟糕。因此所有内容(密码、电子邮件等)都会显示出来。

我的问题是:是否可以在 doctrine 中将一个实体标记为受保护,这样就不会使用该实体进行自动加载?在某些情况下它非常方便,但这是一个很大的缺陷。如果不能标记一个实体,是否可以完全停用它,或者在一个集合元素上?

提前致谢

编辑:

class User implements UserInterface
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=180, unique=true)
     */
    private $email;
    /**
     * @var string The hashed password
     * @ORM\Column(type="string")
     */
    private $password;
    /**
     * @ORM\OneToOne(targetEntity="App\Entity\Profile", mappedBy="user", cascade={"persist", "remove"})
     */
    private $profile;

那里有 getter 和 setter。

还有一个配置文件class,即所有关系的接口。它具有一对一的关系。

class Profile
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\OneToOne(targetEntity="App\Entity\User", inversedBy="profile", cascade={"persist", "remove"})
     * @ORM\JoinColumn(nullable=false)
     */
    private $user;

getter 和 setter 就在那里。

class Event
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="datetime")
     */
    private $date;


    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Profile", inversedBy="ownedEvents")
     * @ORM\JoinColumn(nullable=false)
     */
    private $profile;

    /**
     * @ORM\OneToMany(targetEntity=Post::class, mappedBy="event", orphanRemoval=true)
     */
    private $posts;

问题是,此配置文件已加载,并且用户... 下面是控制器函数。但是序列化是在一个额外的方法中发生的。

public function getUnreactedEvents(): JsonResponse{
        $events = $this->getDoctrine()
            ->getManager()
            ->getRepository(Event::class)
            ->getUnreactedEvents($this->profileUtils->getLoggedInProfileFromDatabase()->getId());
        return new JsonResponse($this->eventUtils->eventsToArray($events));
    }

这里是to数组函数。 (有一个基数class所以有两个方法:

\Utils class:

\Utils class:
    public function eventsToArray($events): array{
        return $this->toArray($events, array("usrEvntSts"));
    }

\Base class:
   protected function toArray($objects, $fieldsToBeRemoved): array{
        $normalizers = [new DateTimeNormalizer(), new ObjectNormalizer()];
        $serializer = new Serializer($normalizers);

        if(!is_array($objects)){
            $objects = array($objects);
        }

        //normalizes the objects object, for circular references, returns id of the object
        //doctrine comes with own array format
        $objectsArray = $serializer->normalize($objects, 'array', [
            'circular_reference_handler' => function ($object) {
                return $object->getId();
            }
        ]);

        //some keys have to be erased from the event response
        foreach ($objectsArray as $key => $object) {
            if (method_exists($objects[0], "getProfile")){
                /** @var Profile $profile */
                $profile = $objects[$key]->getProfile();
                unset($objectsArray[$key]["profile"]);
                $objectsArray[$key]["profile"]['id'] = $profile->getId();
            }
            foreach ($fieldsToBeRemoved as $field){
                unset($objectsArray[$key][$field]);
            }
        }

        return $objectsArray;
    }
}

如您所见,我的第一个想法是只删除该字段。但是在我添加了一个新的实体关系(帖子)之后,它也有一个所有者配置文件。用户 class 再次加载...

输出:

    {
        "id": 1,
        "name": "xcvxycv",
        "date": "2020-06-28T18:08:55+02:00",
        "public": false,
        "posts": [
            {
                "id": 1,
                "date": "2020-06-30T00:00:00+02:00",
                "content": "sfdnsdfnslkfdnlskd",
                "profile": {
                    "id": 2,
                    "user": {
                        "id": 2,
                        "email": "alla",
                        "username": "alla",
                        "roles": [
                            "ROLE_USER"
                        ],
                        "password": "$argon2id$v=19$m=65536,t=4,p=1$a01US1dadGFLY05Lb1RkcQ$npmy0HMf19Neo/BnMqXGwkq8AZKVSCAEmDz8mVHLaQ0",
                        "salt": null,
                        "apiToken": null,
                        "profile": 2
                    },
                    "username": "sdfsdf",
                    "usrEvntSts": [],
                    "ownedEvents": [
                        {
                            "id": 3,
                            "name": "blaaaa",
                            "date": "2020-06-28T18:08:55+02:00",
                            "profile": 2,
                            "public": false,
                            "usrEvntSts": [],
                            "posts": [
                                {
                                    "id": 2,
                                    "date": "2020-06-30T00:00:00+02:00",
                                    "content": "sfdnsdfnslkfdnlskd",
                                    "profile": 2,
                                    "event": 3,
                                    "comments": []
                                }
                            ]
                        },
                        

它一直在继续......

我建议使用 JMSSerializerBundle for that. It is a widely used bundle, also in huge API's. You can exactly configure which properties should be exposed and which not. You can also build groups for exposing properties and use a specific exclusion strategy. Check the documentation 获取更多信息。

提示:还要检查 Limiting serialization depth 是否有深层嵌套对象。