Giter VIP home page Giter VIP logo

encryptbundle's Introduction

SpecShaper Encrypt Bundle

A bundle to handle encoding and decoding of parameters using OpenSSL and Doctrine lifecycle events.

Features include:

  • V4 is will be Symfony 7 PHP 8.2
  • V3 is Symfony 5.4|6 PHP 8. I'm moving my projects to symfony 7 so will not maintain after this v3.2.
  • V2 is Symfony 5 not maintained.
  • v1 is Symfony 3.4 not maintained.
  • Uses OpenSSL
  • Uses Lifecycle events

Features road map:

  • Create a factory method to expand for different encryptors
  • Create a twig function to decrypt encoded values
  • Expand parameters to allow selection of encoding method
  • Create CLI commands to encrypt and decrypt the entire database
  • Handle DateTime data types via the bundle.

License

This bundle is under the MIT license. See the complete license in the bundle:

Resources/meta/LICENSE

About

EncryptBundle has been written for the Parolla Plugins and Parolla websites to encode users private data. The bundle is expanded in a larger gdpr-bundle.

Reporting an issue or a feature request

Issues and feature requests are tracked in the Github issue tracker.

When reporting a bug, it may be a good idea to reproduce it in a basic project built using the Symfony Standard Edition to allow developers of the bundle to reproduce the issue by simply cloning it and following some steps.

Installation

Step 1: Install from package

Open a command console, enter your project directory and execute the following command to download the latest development version of this bundle:

$ composer require specshaper/encrypt-bundle dev-master

This command requires you to have Composer installed globally, as explained in the installation chapter of the Composer documentation.

Step 2: Enable the bundle

The receipe will create a package config file under config/packages/spec_shaper_encrypt.yaml.

If required, enable the bundle by adding it to the list of registered bundles in the config/bundles.php file of your project:

<?php

return [
    ...
    SpecShaper\EncryptBundle\SpecShaperEncryptBundle::class => ['all' => true],
];

Step 2: Configure the bundle

Generate a 256-bit key using the command provided in the bundle.

$ bin/console encrypt:genkey

Copy the key into your .env file.

###> encrypt-bundle ###
SPEC_SHAPER_ENCRYPT_KEY= change_me!
###< encrypt-bundle ###

Maker will have created a packages yaml file. The key is resolved in there.

# app/config/packages/spec_shaper_encrypt.yaml
spec_shaper_encrypt:
  encrypt_key: '%env(SPEC_SHAPER_ENCRYPT_KEY)%'
  is_disabled: false # Turn this to true to disable the encryption.
  connections:   # Optional, define the connection name(s) for the subscriber to listen to.
    - 'default'
    - 'tenant'
  subscriber_class: App\Subscriber\MyCustomSubscriber # Optional to override the bundle Doctrine event subscriber.
  encryptor_class: App\Encryptors\MyCustomEncryptor # Optional to override the bundle OpenSslEncryptor.
  annotation_classes: # Optional to override the default annotation/Attribute object.
    - App\Annotation\MyAttribute

You can disable encryption by setting the 'is_disabled' option to true. Decryption still continues if any values contain the <ENC> suffix.

You can extend the EncryptBundle default Subscriber and override its methods. Use the 'subscriber_class' option to point the bundle at your custom subscriber.

If you want to define your own annotation/attribute, then this can be used to trigger encryption by adding the annotation class name to the 'annotation_classes' option array.

You can pass the class name of your own encyptor service using the optional encryptorClass option.

Alternative EncryptKeyEvent

The EncryptKey can be set via a dispatched event listener, which overrides any .env or param.yml defined key. Create a listener for the EncryptKeyEvents::LOAD_KEY event and set your encryption key at that point.

Step 3: Create the entities

Add the Encrypted attribute class within the entity.

<?php
...
use SpecShaper\EncryptBundle\Annotations\Encrypted;

Add the attribute #[Encrypted] to the properties you want encrypted.

Note that the legacy annotation '@Encrypted' in the parameters is deprecated and will be discontinued in the next major update.

<?php

    /**
     * A PPS number is always 7 numbers followed by either one or two letters.
     * 
     * @ORM\Column(type="string")
     */
    #[Encrypted]
    protected string $taxNumber;
    
    /**
     * True if the user is self employed.
     * 
     * @ORM\Column(type="string", nullable=true)
     */
    #[Encrypted]
    protected ?bool $isSelfEmployed;
    
    /**
     * Date of birth
     * 
     * @Encrypted
     * Note that the above Encrypted property is a legacy annotation, and while
     * it still is supported, it will be deprecated in favour of Attributes.
     * 
     * @ORM\Column(type="string", nullable=true)
     */
    protected ?String $dob;
   

Where encrypting a field you will need to set the column type as string.

Your getters and setters may also need to be type declared.

For example, boolean should either be return declared bool, or return a bool using a ternary method.

<?php
    /**
     * Get isSelfEmployed
     *
     * @return boolean
     */
    public function isSelfEmployed(): bool
    {
        return $this->isSelfEmployed;
    }

    /**
     * Get isSelfEmployed
     *
     * @return boolean
     */
    public function isSelfEmployed(): bool
    {
        return ($this->isSelfEmployed == 1 ? true: false);
    }

For DateTime parameters store the date as a string, and use the getters and setters to convert that string.

You may also need to create a DataTransformer if you are using the parameter in a form with the DateType form type.

Step 4: General Use

The bundle comes with an DoctrineEncryptSubscriber. This subscriber catches the doctrine events onLoad, onFlush and postFlush.

The onLoad event subscriber will decrypt your entity parameter at loading. This means that your forms and form fields will already be decrypted.

The onFlush and postFlush event subscribers will check if encryption is enabled, and encrypt the data before entry to the database.

So, in normal CRUD operation you do not need to do anything in the controller for encrypting or decrypting the data.

Step 5: Decrypt in services and controllers

You can of course inject the EncryptorInterface service any time into classes either by using autowiring or defining the injection in your service definitions.

<?php
    use SpecShaper\EncryptBundle\Encryptors\EncryptorInterface;
    ...
    /**
     * @var SpecShaper\EncryptBundle\Encryptors\EncryptorInterface;
     */
    private $encryptor;
    ...
    
    // Inject the Encryptor from the service container at class construction
    public function __construct(EncryptorInterface $encryptor)
    {
        $this->encryptor = $encryptor;
    }
    
    // Inject the Encryptor in controller actions.
    public function editAction(EncryptorInterface $encryptor)
    {
        ...
        // An example encrypted value, you would get this from your database query.
        $encryptedValue = "3DDOXwqZAEEDPJDK8/LI4wDsftqaNCN2kkyt8+QWr8E=<ENC>";
        
        $decrypted = $encryptor->decrypt($encryptedValue);
        ...
    }

Or you can dispatch the EncryptEvent.

<?php
    ...
    use SpecShaper\EncryptBundle\Event\EncryptEvent;
    use SpecShaper\EncryptBundle\Event\EncryptEvents;
    use Symfony\Component\EventDispatcher\EventDispatcherInterface;
    ...
    
    public function indexAction(EventDispatcherInterface $dispatcher)
    {
        ...
        // An example encrypted value, you would get this from your database query.
        $event = new EncryptEvent("3DDOXwqZAEEDPJDK8/LI4wDsftqaNCN2kkyt8+QWr8E=<ENC>");

        $dispatcher->dispatch(EncryptEvents::DECRYPT, $event);
        
        $decrypted = $event->getValue();
    }

Step 5: Decrypt in templates

If you query a repository using a select with an array result then the doctrine onLoad event subscriber will not decrypt any encrypted values.

In this case, use the twig filter to decrypt your value when rendering.

{{ employee.bankAccountNumber | decrypt }}

Commands

You have already seen the command to generate a encryption key:

$ bin/console encrypt:genkey

You can decrypt/encrypt the entire database using the following

$ bin/console encrypt:database decrypt connection

The requried argument should be be decrypt or encrypt.

There is an option to define the database connection if you employ multiple connections in your application.

encryptbundle's People

Contributors

achterin avatar brysn avatar danabrey avatar feymo avatar fournyp avatar jameskfry avatar joelterry-promenade avatar mogilvie avatar russelomua avatar spacecodeit avatar tonysma avatar travismarkvh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

encryptbundle's Issues

Embeddable support

Doctrine Embeddables feature allows to make a some kind of sub-entity.

I try to make a fork and i have an error from github "πŸ¦„ This page is taking too long to load.", so i make a patch
and here it is:

Subject: [PATCH] fix doctrine embeddable
---
Index: Subscribers/DoctrineEncryptSubscriber.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/Subscribers/DoctrineEncryptSubscriber.php b/Subscribers/DoctrineEncryptSubscriber.php
--- a/Subscribers/DoctrineEncryptSubscriber.php	(revision edf06dad2de0abb040e1d0757b06ae5bf2938dac)
+++ b/Subscribers/DoctrineEncryptSubscriber.php	(revision 2afc3213b2756680f435ab62ffb49f0005a132d2)
@@ -177,7 +177,7 @@
                 $changeSet = $unitOfWork->getEntityChangeSet($entity);
 
                 // Encrypt value only if change has been detected by Doctrine (comparing unencrypted values, see postLoad flow)
-                if (isset($changeSet[$refProperty->getName()])) {
+                if (isset($changeSet[$key])) {
                     $encryptedValue = $this->encryptor->encrypt($value);
                     $refProperty->setValue($entity, $encryptedValue);
                     $unitOfWork->recomputeSingleEntityChangeSet($em->getClassMetadata(get_class($entity)), $entity);
@@ -187,7 +187,7 @@
                         $refProperty->setValue($entity, $value);
                     } else {
                         // Will be restored during postUpdate cycle
-                        $this->rawValues[$oid][$refProperty->getName()] = $value;
+                        $this->rawValues[$oid][$key] = $value;
                     }
                 }
             } else {
@@ -196,7 +196,7 @@
                 $refProperty->setValue($entity, $decryptedValue);
 
                 // Tell Doctrine the original value was the decrypted one.
-                $unitOfWork->setOriginalEntityProperty($oid, $refProperty->getName(), $decryptedValue);
+                $unitOfWork->setOriginalEntityProperty($oid, $key, $decryptedValue);
             }
         }
 
@@ -222,7 +222,7 @@
     }
 
     /**
-     * @return ReflectionProperty[]
+     * @return array<string, ReflectionProperty>
      */
     protected function getEncryptedFields(object $entity, EntityManagerInterface $em): array
     {
@@ -236,9 +236,9 @@
 
         $encryptedFields = [];
 
-        foreach ($meta->getReflectionProperties() as $refProperty) {
+        foreach ($meta->getReflectionProperties() as $key => $refProperty) {
             if ($this->isEncryptedProperty($refProperty)) {
-                $encryptedFields[] = $refProperty;
+                $encryptedFields[$key] = $refProperty;
             }
         }

ClassMetadata::getReflectionProperties() returns valid keys of properties
Screenshot 2023-02-12 at 13 57 33

and UnitOfWork is store original entity properties in same format (UnitOfWork::setOriginalEntityProperty will work correct):
Screenshot 2023-02-12 at 13 59 28

thanx

Encrypted value on entity connected through relation is updated at every flush

We've got the following setup:

Entity A

  • Encrypted property: Enc
  • OneToOne relation with Entity A

Entity B

  • OneToOne relation with Entity B

While flushing the entity manager, without any changes on either entity A or entity B; the ORM sees a changed value for the Enc property on Entity A through the relation with Entity B.

This is due to the "oid" of the Entity A relation being different from the "oid" of the "actual?" entity A.
This "oid" seems to be a 3 digit number instead of a hexadecimal format.

In short, having a relationship with an entity that has encrypted properties will not work as expected.

On every flush() the value will be rewritten (with a value that can be decrypted, so it's usable) to the database with a transaction, update and commit command.

Symfony 6.4 disabled annotations in framwork bundle and site crashed.

in symfony 6.4 annotations are deprecated and disabled in framework but in SpecShaper\EncryptBundle\Command\EncryptDatabaseCommand service Doctrine\Common\Annotations\Reader is autoloading as annotationReader which is disabled result in site crashed with error

Cannot autowire service "SpecShaper\EncryptBundle\Command\EncryptDatabaseCommand": argument "$annotationReader" of method "__construct()" references interface "Doctrine\Common\Annotations\Reader" but no such service exists. Did you create a class that implements this interface?

Identifier "default" as connection name is mandatory?

Hey,

i installed version v2.0.1 of EncryptBundle and i'm getting the following error message:

!!    The Doctrine connection "default" referenced in service "SpecShaper\Encrypt  
!!    Bundle\Subscribers\DoctrineEncryptSubscriberInterface" does not exist. Avai  
!!    lable connections names: ..., ..., ...

I do not have a connection named "default" for reasons. Can i configure the bundle in any way not to use the connection named "default"? In my doctrine.yaml i configured the default connection this way:

doctrine:
    dbal:
        default_connection: myconnection

When configuring a connection named "default" the error is gone, but i would like not to have a connection named "default".

Thank you for your work :-)

Symfony 4 - encrypt:genkey/encrypt_key

Hello, me again.

Symfony version: 4.2.x

So before i forget or anything. Since I want to make my MIT project public later (where Your package is a part of it).

After installing Your package there are 3 problems.

  • Once package is installed it breaks the page

I'm not having screenshots or anything, but when Your install the package and without doing anything - refresh the page You get an exception that encrypt_key was not found

I know You got to provide one by generating it via console but here actually comes next problem:

  • Cannot generate key before fixing the broken page - look above

The encrypt:genkey command is wont work untill the encrypt_key will be added in yaml

  • Encrypt command is not working in CLI

(After fixing) When i try to generate key via bin/console encrypt:genkey I get:
image

Summary

  • I cannot make the page run because i don't have the key
  • I cannot make the key because the page is not working
  • After all I cannot run the cli command to generate key
  • What's more online generators that generate 256bit keys are kinda incompatibile (keept getting exceptions about to short/to long key)

I've generally made my page run again but just saying that there are some problems

[Question] What do You need `DoctrineEncryptSubscriber::postLoad` for?

Hello,

First of all - this might not be a bug - not sure about this. I just need an info to investigate that further.

You've recently added the event which allows to change the encryption key - that works fine but got a problem related to this now.
So I've spent over 1h trying to find out what's going on.

What is the purpose of the block in DoctrineEncryptSubscriber:

    /**
     * Listen a postLoad lifecycle event. Checking and decrypt entities
     * which have @Encrypted annotations
     *
     * @param LifecycleEventArgs $args
     * @throws EncryptException
     */
    public function postLoad(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();
        $em = $args->getEntityManager();

        if (!$this->hasInDecodedRegistry($entity)) {
            if ($this->processFields($entity, $em, false)) {
                $this->addToDecodedRegistry($entity);
            }
        }
    }

I believe it's needed in case when You save an entity and You still use it later on, like this:

$this->em->persist($entity);
$this->em->flush();

$entity->getValueForSomeReason();

Am I correct with understanding of this?

Last update broke symfony <6.1 support

Since this bundle is now extenting AbstractBundle support for symfony versions prior 6.1 is broken.
The symfony documentation states:

If your bundle must be compatible with previous Symfony versions you have to extend from the Bundle instead.

As AbstractBundle was introduced in v6.1 you'll get the following error trying to install the latest version on older symfony versions:

[critical] Uncaught Error: Class "Symfony\Component\HttpKernel\Bundle\AbstractBundle" not found

Plaintext recovery through padding oracle (chosen-ciphertext attack)

The Vulnerability

The encryption method is hard-coded to aes-256-cbc:

public const METHOD = 'aes-256-cbc';

The decryption algorithm is implemented here:

public function decrypt(?string $data): ?string
{
// If the value is an object or null then ignore
if (is_null($data)) {
return null;
}
// If the value does not have the suffix <ENC> then ignore.
if (DoctrineEncryptSubscriberInterface::ENCRYPTED_SUFFIX !== substr($data, -5)) {
return $data;
}
$data = substr($data, 0, -5);
// If the data was just <ENC> the return null;
if (empty($data)) {
return $data;
}
$key = $this->getSecretKey();
$data = base64_decode($data);
$ivsize = openssl_cipher_iv_length(self::METHOD);
$iv = mb_substr($data, 0, $ivsize, '8bit');
$ciphertext = mb_substr($data, $ivsize, null, '8bit');
return openssl_decrypt(
$ciphertext,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$iv
);
}

This code performs decryption using AES-256-CBC without authentication. By default, OpenSSL uses PKCS#7 padding. This is susceptible to chosen-ciphertext attacks. Specifically, the padding oracle attack.

Exploit Scenario

Alice stores her sensitive data in an app that uses this EncryptBundle. The app stores the encrypted data in a separate database server. The app has a migration utility intended to facilitate key rotation that the developer leaves in the public web root. As a consequence of how this code functions, it returns an HTTP 200 OK response if the decryption succeeds and an HTTP 500 Internal Server Error if an error occurs.

Bob is a hacker that has privileged access to the database server and is aware of the migration utility on the app webserver. The expectation for a library like this is that Bob would be unable to decrypt Alice's records. However, he is able to update her encrypted data (and mark it as a candidate for migration), query Alice's record with the migration utility, and observe if there's a padding error. By doing so, he is able to recover the plaintext for her data without access to the app server's encryption key.

Recommended Mitigation

  1. Use an authenticated encryption method, such as AES-GCM, instead of AES-CBC. Be extra sure to pass the column name (and any other metadata) as the associated data.

Entity creation requires at least one property with the "Encrypted" attribute to have a non-null value

Description:
I encountered an issue while creating a new entity with properties that have the "Encrypted" attribute. The problem is that the entity creation process requires at least one property with the "Encrypted" attribute to have a non-null value, even when other non-encrypted properties have default null values.

Steps to reproduce:

Define a new entity with multiple properties, including at least one property annotated with the "Encrypted" attribute (e.g., @Encrypted).
Set default values for non-encrypted properties to null in the entity definition.
Attempt to create a new instance of the entity, providing values only for non-encrypted properties while leaving the property with the "Encrypted" attribute as null.
Expected behavior:
The system should allow the creation of the entity with null values for non-encrypted properties and automatically handle the encryption process when a value is provided for the "Encrypted" property during the entity creation process.

Actual behavior:
Currently, when trying to create the entity without a value for the property with the "Encrypted" attribute, the system throws an error or exception, indicating that at least one property with the "Encrypted" attribute should have a non-null value.

Error message and stackTrace:
Undefined array key 2760

/app/vendor/specshaper/encrypt-bundle/src/Subscribers/DoctrineEncryptSubscriber.php:201
/app/vendor/specshaper/encrypt-bundle/src/Subscribers/DoctrineEncryptSubscriber.php:100
/app/vendor/symfony/doctrine-bridge/ContainerAwareEventManager.php:63
/app/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:3587
/app/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:420
/app/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:403
/app/tests/DataFixtures/Common/CommonAccountFixture.php:71
/app/vendor/doctrine/data-fixtures/src/Executor/AbstractExecutor.php:123
/app/vendor/doctrine/data-fixtures/src/Executor/ORMExecutor.php:26
/app/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:272

so to fix this issue I changed method processFields in DoctrineEncryptSubscriber

..... if ($isInsert && isset($this->rawValues[$oid])) { foreach ($this->rawValues[$oid] as $prop => $rawValue) { $refProperty = $meta->getReflectionProperty($prop); $refProperty->setValue($entity, $rawValue); } unset($this->rawValues[$oid]); } ........

Possible issues with multiples encrypted fields on one entity

Firstly, I would like to thank you for this project.
I created something similar for one of my project, but I'm using ciphersweet for the encryption & keep the ability to search (at least partly) on encrypted field (using blind indexes). So I looked at your code to improve mine. I was using different events, and your way of doing is more efficient.

On my project, by reusing some of your logic inside the Doctrine Subscriber I came across an issue. When an entity has multiple encrypted fields, only the "last" field is encrypted for INSERT.

I didn't try on your project because I did not use it as is. But I think you may have the same issue.

This issue is on the DoctrineEncryptSubscriber Line 183.

On my side, it didn't work for the insert because of the setValue that come after. When you have multiple encrypted field, by setting the value here, it seems to messes up the recomputeSingleEntityChangeSet that detect another change. So I end up with the last field of my entity to be encrypted.

So the solution is to re-set the object value in the end or just like the postUpdate.

symfony/console 6.1 deprecations

Hello !

Since I updated my project to Symfony 6.1, I have these two deprecation warnings in the profiler :

Since symfony/console 6.1: Relying on the static property "$defaultName" for setting a command name is deprecated. Add the "Symfony\Component\Console\Attribute\AsCommand" attribute to the "SpecShaper\EncryptBundle\Command\GenKeyCommand" class instead.

The "Symfony\Component\Console\Command\Command::$defaultName" property is considered final. You should not override it in "SpecShaper\EncryptBundle\Command\GenKeyCommand".

Thank you

The package expects a Doctrine connection named `default`

Hi there!

Just wanted to try out the package as the multi-connection support was interesting for the project I'm working on, and I had the following error pop up when running the package's recipe upon composer install:

!!    In RegisterEventListenersAndSubscribersPass.php line 91:
!!                                                                                 
!!    The Doctrine connection "default" referenced in service "SpecShaper\Encrypt  
!!    Bundle\Subscribers\DoctrineEncryptSubscriber" does not exist. Available con  
!!    nections names: "regional", "core".  

As you can see, neither of our connections are named "default", which crashes the app until the connections are defined in the configuration.

I don't have time for a PR on this at this very moment, but I expect looking at the doctrine.dbal.default_connection configuration could fix this issue, in order to make the install process smoother.

If I get some time to look into it before you do I might just make a PR for it.

Cheers! :)

Twig decryption not automatic since v3.2.0

In version 3.1.0, encrypted values ​​were automatically decrypted in "twig" templates.
Since version 3.2.0, I have to use the "decrypt" filter manually.
I don't know which version has the correct behavior.

Twig : 3.10.3
Symfony : 6.4.8
PHP : 8.2
Doctrine : 2.19.5

Support for Doctrine/ORM 3.x

I have a Symfony 6 project with Doctrine/ORM 3, however the bundle only supports ORM 2.11. Is there a way to get it to work with version 3?

Cannot instantiate interface SpecShaper\EncryptBundle\Encryptors\EncryptorInterface

When instantiate the EncryptorInterface in a controller, Symfony throws an exception:

CRITICAL11:11:00 php Cannot instantiate interface SpecShaper\EncryptBundle\Encryptors\EncryptorInterface
CRITICAL11:11:00 request Uncaught PHP Exception Symfony\Component\Debug\Exception\FatalThrowableError: "Cannot instantiate interface SpecShaper\EncryptBundle\Encryptors\EncryptorInterface" at /Users/jeordy/Sites/TuffelCrm/src/CrmBundle/Controller/MagentoController.php line 41

Symfony 4 - Encrypt the same string for 2 differents encryptage

Hello
I tried your bundle and it works very well.
On the other hand I was wondering how to do a findBy on a thong. Currently if I try to encode a string I end up with 2 different encoding so how to make a find:)

Here is an example of my code:
`

/**
 * @return Response
 */
public function index(EncryptorInterface $encryptor)
{
    var_dump($encryptor->encrypt('gthomas'));
    var_dump($encryptor->encrypt('gthomas'));
    $user = $this->getDoctrine()->getRepository(User::class)->findByUsername($encryptor->encrypt('gthomas'));`

Thank you

MariaDB syntax violation

Setup

PHP 8.2.0, Windows 10 x64, ThreadSafe
Symfony 6.2.11 - env: dev
MariaDB 10.11.3-MariaDB-1
Doctrine ORM 2.15.2

Complete composer.json

{
    "type": "project",
    "license": "proprietary",
    "minimum-stability": "stable",
    "prefer-stable": true,
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "doctrine/annotations": "^1.0",
        "doctrine/doctrine-bundle": "^2.7",
        "doctrine/doctrine-migrations-bundle": "^3.2",
        "doctrine/orm": "^2.13",
        "phpdocumentor/reflection-docblock": "^5.3",
        "phpstan/phpdoc-parser": "^1.9",
        "specshaper/encrypt-bundle": "dev-master",
        "spomky-labs/otphp": "^10.0",
        "stripe/stripe-php": "v10.*",
        "symfony/asset": "6.2.*",
        "symfony/console": "6.2.*",
        "symfony/dotenv": "6.2.*",
        "symfony/expression-language": "6.2.*",
        "symfony/flex": "^2",
        "symfony/form": "6.2.*",
        "symfony/framework-bundle": "6.2.*",
        "symfony/http-client": "6.2.*",
        "symfony/intl": "6.2.*",
        "symfony/mailer": "6.2.*",
        "symfony/mime": "6.2.*",
        "symfony/monolog-bundle": "^3.0",
        "symfony/notifier": "6.2.*",
        "symfony/process": "6.2.*",
        "symfony/property-access": "6.2.*",
        "symfony/property-info": "6.2.*",
        "symfony/proxy-manager-bridge": "6.2.*",
        "symfony/runtime": "6.2.*",
        "symfony/security-bundle": "6.2.*",
        "symfony/serializer": "6.2.*",
        "symfony/string": "6.2.*",
        "symfony/translation": "6.2.*",
        "symfony/twig-bundle": "6.2.*",
        "symfony/uid": "6.2.*",
        "symfony/validator": "6.2.*",
        "symfony/web-link": "6.2.*",
        "symfony/webpack-encore-bundle": "^1.16",
        "symfony/yaml": "6.2.*",
        "twig/extra-bundle": "^2.12|^3.0",
        "twig/twig": "^2.12|^3.0"
    },
    "config": {
        "allow-plugins": {
            "composer/package-versions-deprecated": true,
            "symfony/flex": true,
            "symfony/runtime": true
        },
        "optimize-autoloader": true,
        "preferred-install": {
            "*": "dist"
        },
        "sort-packages": true
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Tests\\": "tests/"
        }
    },
    "replace": {
        "symfony/polyfill-ctype": "*",
        "symfony/polyfill-iconv": "*",
        "symfony/polyfill-php72": "*",
        "symfony/polyfill-php73": "*",
        "symfony/polyfill-php74": "*",
        "symfony/polyfill-php80": "*"
    },
    "scripts": {
        "auto-scripts": {
            "cache:clear": "symfony-cmd",
            "assets:install %PUBLIC_DIR%": "symfony-cmd"
        },
        "post-install-cmd": [
            "@auto-scripts"
        ],
        "post-update-cmd": [
            "@auto-scripts"
        ]
    },
    "conflict": {
        "symfony/symfony": "*"
    },
    "extra": {
        "symfony": {
            "allow-contrib": false,
            "require": "6.2.*"
        },
        "public-dir": "www"
    },
    "require-dev": {
        "roave/security-advisories": "dev-latest",
        "phpunit/phpunit": "^9.5",
        "symfony/browser-kit": "6.2.*",
        "symfony/css-selector": "6.2.*",
        "symfony/debug-bundle": "6.2.*",
        "symfony/maker-bundle": "^1.0",
        "symfony/phpunit-bridge": "^6.1",
        "symfony/stopwatch": "6.2.*",
        "symfony/web-profiler-bundle": "6.2.*"
    }
}

When error occurs

Error occurs on execution of this command:
php bin/console encrypt:database encrypt

Error that occurs:
image

Setting encryptor and encryptKey dynamically via event

Hello,

Regarding the: #15

So I generally struggled for a longer moment with assigning container parameters dynamically but there is no simple way to go with this, would need to really make some ugly stuff which I want to avoid so I got other solution, which I've briefly tested and works.

Let me know if You would be interested in having this in Your project and if So then I will provide MR in some free time.

Solution: Adding new Event BeforeCreateEncryptionServiceListener
image

If You don't want this at all then please close the issue and I will just add this to my project via composer patcher package.

Oh yeah there is a bug on screenshot - but anyway just tested it briefly so it doesnt matter atm.

Add non-annotation mapping

I'd like to use this bundle, but I don't use annotations in my entities.
It would be nice to get mapping in XML (and maybe in YAML too, but this is going to be dropped in Doctrine 3)

Update doctrine entity changeset data onLoad, preFlush and postFlush.

When using this bundle in conjunction with DamienHarper/auditor-bundle one can observe that the entity changeset produced is plain wrong.

What I expect to see on insert:
Old value: null
New value: encrypted new value

Instead you get this:
Old value: encrypted new value
New value: decrypted new value

What I expect to see on change:
Old value: encrypted old value
New value: encrypted new value

Instead you get this:
Old value: decrypted new value
New value: encrypted new value

I've spend quite some time to figure out how this can be fixed and tracked it down to two problems:

  1. onFlush is the wrong event to encrypt on change as the changeset is already created by doctrine with decrypted values. preFlush would be better as this allows to populate the entity-properties with the encrypted values and doctrine will create the changeset just fine.
  2. onLoad needs to set the encrypted value as original data, not the decrypted one.

Right now I'm stuck sadly because it seems that MySQL and Postgres work differently. With MySQL I can rely on new entities not being in the doctrine entity-map, but the get inserted there when pre-flush is fired when using Postgres.

I think the current behavior is not desired as this will result in sensitive data being leaked as they get written to the audit tables in plaintext. On top of that the changes written to audit tables are plain wrong and are more or less useless.

I am looking forward to your opinion on this matter.

Error for encrypt_key

Hello I use symfony 5.4 so I had to install version 2 of the bundle I have a problem I created like asking for the parameter.yaml file or in yml it changes nothing in config/packages it does not find the key it is however present in my .env file

Thank you for help

The bundle specshaper\encrypt-bundle requires a parameter.yml value for "encrypt_key"
Use cli command "php bin/console encrypt:genkey" to create a key.

Manually decrypt value

Is there a simple way to get decrypted value having secret key and encrypted value? Like
php bin/console decrypt:value <encrypted value> [--key <secret key>]
And how do I change key (decrypt and than encrypt with new key all data) if it's is compromised?

Symfony 4 - Encrypted and decrypted key is the same.

Hello,

I've just added Your bundle to my symfony project 4.3 i think. I will skip issues with key generating but I've managed to make 256 bit one.

Now I've got a problem that actually encrypted and decrypted key is the same.
Am i doing something wrong or any idea?

image
image

I'm just doing tests now, so You can recreate it with this key:
encrypt_key: 'kXp2s5v8y/A?D(G+KbPehVmYq3t6whVmYq3tt6wShVw9z'
encrypted string: test

Same for twig:
{{ 'WlCj153fQK1yzp1qZXByLBaUL9T2vw7nq7fbSXruZ8Q=' | decrypt }}
Result: WlCj153fQK1yzp1qZXByLBaUL9T2vw7nq7fbSXruZ8Q=

[Malfunction] `onEncryptionLoadKey` purpose gets violated by `postLoad`

Since I want to use Your package in next project I will try to explain the issue the best I can right now.

Flow of data - sending between projects

image

Details

  • I'm using the event, that allows to change the key on fly,
  • Since Your package requires any key to be defined i need to have a dummy key to prevent exceptions, so I have this
parameters:
    encrypt_key: "0000000000000000000000000000000000000000000" # (random key - not used - needs to be set to suppress the encryption bundle error)

Flow of data - user logs in

image

Cron call no 1. - Insert Passwords Groups

foreach($insertRequest->getPasswordsGroupsArrays() as $passwordGroupArray){
   $passwordGroupEntity = PasswordGroup::fromArray($passwordGroupArray);
   ...
   $this->passwordGroupController->save($passwordGroupEntity);
}

Status: Works

Cron call no 2. - Insert Passwords and assign them to groups

foreach($insertRequest->getPasswordsArrays() as $passwordArray){
    $passwordEntity      = Password::fromArray($passwordArray);
    $passwordGroupEntity = $this->passwordGroupController->getOneForId($passwordEntity->getGroupId());
    ...
    $this->passwordController->save($passwordEntity);
}

Status: Does not work

Under which circumstances the problem happens

  1. The passwordGroupEntity is already saved with first cron call, and has correct encryption string from PMS (1),
  2. Now I fetch the passwordGroupEntity from DB
  3. Seems like Your bundle decrypts the values in there
  4. I set the relation between passwordGroupEntity and passwordEntity
  5. I save the passwordEntity
  6. Doctrine messes up encrypts the passwordGroupEntity fields with key stored in yaml which is 0000

Where is Your code related to the issue

  • Class: DoctrineEncryptSubscriber,
  • Method: processFields
  • Code block:
            if($isEncryptOperation) {
                $encryptedValue = $this->encryptor->encrypt($value);
                $refProperty->setValue($entity, $encryptedValue);
            } else {
                $decryptedValue = $this->decryptValue($value);
                $refProperty->setValue($entity, $decryptedValue); 
                //we don't want the object to be dirty immediately after reading
                $unitOfWork->setOriginalEntityProperty($oid, $refProperty->getName(), $value);
            }
  • Specifically - this one line causes the problem, when commented out works fine
$refProperty->setValue($entity, $decryptedValue); 
  • Additionally - this problem makes the overwriting of encryption key via event no longer usable, because right now Your bundle doesn't know that the key will be replaced and uses the one hardcoded.

This makes sense because "Hello how will Your bundle know that key is replaced otherwise?". Yet the current situation makes the onEncryptionLoadKey kinda useless.

End note

I've spent tones of time trying to find out why / where this happens. I cannot propose the solution as I don't fully understand why such thing has place. I have my own solution in project which is saving via plain sql - I can use that in current project, but next one is a no since I want to encrypt entire DB.

`is_disabled` and `encrypt_key` empty

Hello,

No idea if that's bug or planned.

I'm creating spec_sharper_encrypt.yaml. Then I set the content:

spec_shaper_encrypt:
  is_disabled : true

Yet this is not suppressing the Needs a 256-bit key, '0'bit given! error when I have encrypt_key: "" (that's empty on purpose).

No longer work with symfony 6

Uncaught Error: Class "Symfony\Component\HttpKernel\Bundle\AbstractBundle" not found in /vendor/specshaper/encrypt-bundle/src/SpecShaperEncryptBundle.php:16

Made Twig support optional via config

Looks like the Twig support feature can't be disabled at all.

At the same time the Twig bundle is not required in the composer.json.

So, the attempt to use the EncryptBundle on apps without Twig bundle leads to the
Attempted to load class "Twig_Extension" from the global namespace.
error.

Can the Twig support feature be configurable? At lest in supported vesions.

Registering DoctrineEncryptSubscriber as a Doctrine subscriber is deprecated

Here is a deprecation warning for the future Symfony 7.0

deprecation.INFO: User Deprecated: Since symfony/doctrine-bridge 6.3: Registering 
"SpecShaper\EncryptBundle\Subscribers\DoctrineEncryptSubscriber" as a Doctrine subscriber is deprecated. 
Register it as a listener instead, using e.g. the #[AsDoctrineListener] attribute. 
{"exception":"[object] (ErrorException(code: 0): User Deprecated: Since symfony/doctrine-bridge 6.3: Registering 
\"SpecShaper\\EncryptBundle\\Subscribers\\DoctrineEncryptSubscriber\" as a Doctrine subscriber is deprecated. 
Register it as a listener instead, using e.g. the #[AsDoctrineListener] attribute. at /app/vendor/symfony/doctrine-bridge/ContainerAwareEventManager.php:211)"}

Installation error

composer require specshaper/encrypt-bundle dev-master reports error during the recipe

  - Configuring specshaper/encrypt-bundle (>=dev-master): From auto-generated recipe
Executing script cache:clear [KO]
 [KO]
Script cache:clear returned with error code 255
!!  TypeError {#583
!!    #message: "SpecShaper\EncryptBundle\Encryptors\OpenSslEncryptor::__construct(): Argument #2 ($key) must be of type string, null given, called in /home/kr4ft3r/code/symfony/airbridge2/vendor/specshaper/encrypt-bundle/Encryptors/EncryptorFactory.php on line 38"

Request for Symfony 7 compatibility

Hello!

I am currently using your library in my project and I find it very useful. However, I am planning to upgrade my project to Symfony 7 and I noticed that your library does not officially support it yet.

Could you please consider updating your library for compatibility with Symfony 7? This would be greatly beneficial for me and other developers who are planning to use Symfony 7.

Thank you for your time and consideration.

Best regards

Encryption works but not persisted

Hello,
I noticed encryption stopped persisting into database after upgrading to dev-master for the insert operations.
$encryptedValue seems ok on line DoctrineEncryptSubscriber.php:182 and it persisted fine if I comment line 203.
But unencrypted value is not persisted if I keep line 203 working.
Seems like it's related to #35 (comment)

"doctrine/orm": "2.14",
"doctrine/doctrine-bundle": "2.8.3"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.