如何防止在 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 是否有深层嵌套对象。
我的问题比较简单,但是在网上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 是否有深层嵌套对象。