php - Symfony 4 - 使用服务加密/解密理论中使用的字段?

标签 php symfony encryption symfony4

所以我的数据库中有实体的字段

class Person
{
    // other fields

    /**
     * @var string
     *
     * @ORM\Column(name="last_name", type="string", length=50, nullable=false)
     */
    private $lastName;

    /**
     * @var string
     *
     * @ORM\Column(name="first_name", type="string", length=50, nullable=false)
     */
    private $firstName;

   // getters and setters
}

我有一个名为 SecureEncryptor 的服务。它具有 Decrypt() 和 Encrypt() 函数 - 基本上你只需将加密/未加密(分别)字符串传递给它,它就会执行相应的操作。

问题是我不确定如何将该服务与实体结合使用 - 特别是在考虑表单(类型)时。我的意思是我知道我可以只获取该字段并调用 Decrypt 函数,但这不适用于绑定(bind)到 Person 实体的类型。

我开始为解密的人制作一个单独的实体,然后我会在处理数据库时切换它,但这似乎是错误的。我的另一个想法是从实体调用服务,但我也读过这也是错误的。

有任何想法吗?

编辑:

这基本上就是我想要做的:
$builder->get('dateOfBirth')
   ->addModelTransformer(new CallbackTransformer(
       function ($encryptedDOB) {
           return $this->encryptor->decrypt($encryptedDOB, salt); // How do I get the salt value here?
       },
      function ($decryptedDOB) {
         return $this->encryptor->encrypt($decryptedDOB, salt); // How do I get the salt value here?
      }
 ));

或者可能在此步骤之前解密/加密数据,但不确定如何完成。

编辑 2:

我找到了 this这表明您可以在 PRE_SET_DATA 事件中获取实体数据,但您无法在其中添加数据转换器,因此不确定它是如何工作的。

最佳答案

经过三天的困惑 - 可能是 20 多个小时的挫折之后,我终于找到了正确的方法来做到这一点。 Entity Event Listeners

所以我做了以下改动

app\config\services.yaml

parameters:
    ...
    encryption_key: '%kernel.project_dir%/path/to/my/key'

services:
    ...
    App\EventListeners\PatientListener:
        arguments: [@session]
        tags:
            - { name: doctrine.event_listener, event: prePersist }
            - { name: doctrine.event_listener, event: preUpdate }
            - { name: doctrine.event_listener, event: postLoad }

然后我做了服务
<?php

namespace App\EventListeners;

use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use App\Entity\Patients;
use ParagonIE\Halite\HiddenString;
use ParagonIE\Halite\KeyFactory;
use ParagonIE\Halite\Symmetric\Crypto as Symmetric;
use ParagonIE\Halite\Symmetric\EncryptionKey;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\Session\Session;

class PatientListener
{
    private $params;
    private $session;
    private $logger;

    public function __construct(ParameterBagInterface $params, 
                                 Session $session, LoggerInterface $logger)
    {
        $this->params = $params;
        $this->session = $session;
        $this->logger = $logger;
    }

    public function prePersist(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();

        if ($entity instanceof Patients)
        {
            $this->encryptFields($entity);
        }
    }

    public function preUpdate(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();

        if ($entity instanceof Patients)
        {
            $this->encryptFields($entity);
        }
    }

    public function postLoad(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();

        if ($entity instanceof Patients)
        {
            $this->decryptFields($entity);
        }
    }

    private function loadKey() : EncryptionKey
    {
        try
        {
            KeyFactory::loadEncryptionKey($this->params->get('encryption_key'));
        }
        catch(\Throwable $e)
        {
            $this->session->getFlashBag()->add('danger', 'Unable to load encryption key!');
            $this->logger->emergency(
                'Unable to lod the encryption key!', array(
                'error' => $e->getMessage(),
            ));
            throw;
        }
    }

    private function encryptFields(Patients $patient)
    {
        $key = $this->loadKey();

        // Encrypt the variables
        $lastName = $this->encrypt('Last Name', $patient->getLastName(), $key);

        // Set the entity variables
        $patient->setLastName($lastName);

        return $patient;
    }

    private function encrypt($fieldName, $value, $key)
    {
        try {
            return Symmetric::encrypt(
                new HiddenString($value),
                $key
            );
        } catch(\Throwable $e)
        {
            $this->session->getFlashBag()->add('danger', 'Unable to encrypt field');
            $this->logger->critical(
                'Unable to encrypt field "'.$fieldName.'" in Patients entity. DB update terminated.', array(
                'error' => $e->getMessage(),
            ));
            throw;
        }

    }

    private function decryptFields(Patients $patient)
    {
        $key = $this->loadKey();
        $id = $patient->getId();

        // Decrypt the variables
        $lastName = $this->decrypt($id, 'Last Name', $patient->getLastName(), $key);

        // Set the entity variables
        $patient->setLastName($lastName);
    }

    private function decrypt($id, $fieldName, $value, $key)
    {
        try
        {
            return Symmetric::decrypt($value, $key);
        }
        catch (\Throwable $e)
        {
            $this->session->getFlashBag()->add('warning', 'Unable to decrypt field');
            $this->logger->warning(
                'Unable to decrypt field "'.$fieldName.'" in Patients entity for ID: '.$id, array(
                'error' => $e->getMessage(),
            ));
        }
    }
}

现在数据在加载到数据库时被加密,在加载到实体时被解密。

这种方式是正确的,因为以任何其他方式执行此操作(自定义原则类型、数据转换器、在 Controller 中执行等)总是有可能如果其他人制作另一种形式或在另一个 Controller 中使用该实体,它可能会离开数据解密(又名非常糟糕)。这种方式可确保数据始终通过原则正确加密和解​​密(除非您执行一些自定义 DQL\SQL,在这种情况下,您可能需要根据自己的操作自行处理)。

关于php - Symfony 4 - 使用服务加密/解密理论中使用的字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52617635/

相关文章:

javascript - Jquery 生成的输入值未发布在表单提交上

php - 获取异常代码列表

php - 在 1and1 托管中发送邮件时遇到问题

php - 你如何在 symfony2 中启动一个 memcached session ?

java - 在 Spring 配置文件中加密密码

php - (PHP, MySQL) Function only works on 1 of 2 cases, cannot find the difference

php - 从 53 个表返回 mysql 数据库的 p_id 和 t_id 记录?

php - 在没有参数的情况下在 PayPal 中返回 url

java - 什么时候加密需要填充?

c - 使用 AES-128/CBC 时 EVP_DecryptFinal_ex 间歇性解密失败