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

Overview

SpecShaper Encrypt Bundle

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

Features include:

  • Master and v2 are Symfony 5.
  • v1 is Symfony 3.4 and not active any more.
  • Uses OpenSSL
  • Uses Lifecycle events

Warning

  • This bundle has not been unit tested.

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 SpecShaper 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: Download the bundle

Open a command console, enter your project directory and execute the following command to download the latest 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

Then, enable the bundle by adding it to the list of registered bundles in the app/AppKernel.php file of your project:


// app/AppKernel.php

// ...
class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // ...
            new SpecShaper\EncryptBundle\SpecShaperEncryptBundle(),
        );
        // ...
    }
    // ...
}

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 ###
ENCRYPT_KEY=
   
    
###< encrypt-bundle ###

   

And resolve in your parameters file.

// app/config/parameters.yml
    ...
    encrypt_key: '%env(resolve:ENCRYPT_KEY)%'

A config file entry is not required, however there are some options that can be configured to extend the bundle.

// app/config/packages/spec_shaper_encrypt.yml
spec_shaper_encrypt:
  # Enter your own subscriber below or comment out to use the bundle subscriber
  # subscriber_class: 'SpecShaper\GdprBundle\Subscribers\GdprSubscriber'
  is_disabled : false
  annotation_classes:
    - 'SpecShaper\EncryptBundle\Annotations\Encrypted'
    - 'AppBundle\Annotations\CustomAnnotation'

You can disable encryption by setting the 'is_disabled' option to true. Decryption still continues if any values contain the 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, then this can be used to trigger encryption by adding the annotation class name to the 'annotation_classes' option array.

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 Annotation entity to the declared classes in the entity.


...
use SpecShaper\EncryptBundle\Annotations\Encrypted;

Add the annotation '@Encrypted' to the parameters that you want encrypted.



    /**
     * A PPS number is always 7 numbers followed by either one or two letters.
     * 
     * @var string
     * @Encrypted
     * @ORM\Column(type="string", nullable=true)
     */
    protected $taxNumber;
    
    /**
     * True if the user is self employed.
     * 
     * @var string | boolean
     * 
     * @Encrypted
     * @ORM\Column(type="string", nullable=true)
     */
    protected $isSelfEmployed;
    
    /**
     * Date of birth
     * 
     * @var string | \DateTimeInterface
     * 
     * @Encrypted
     * @ORM\Column(type="string", nullable=true)
     */
    protected $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.


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

    /**
     * Get isSelfEmployed
     *
     * @return boolean
     */
    public function isSelfEmployed()
    {
        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.

"; $decrypted = $encryptor->decrypt($encryptedValue); ... } ">

    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=
    
     "
    ;
        
        $decrypted = $encryptor->decrypt($encryptedValue);
        ...
    }

Or you can dispatch the EncryptEvent.

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

    ...
    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=
    
     "
    );

        $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 decyrpt any encrypted values.

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

{{ employee.bankAccountNumber | decrypt }}
Comments
  • Setting encryptor and encryptKey dynamically via event

    Setting encryptor and encryptKey dynamically via event

    Hello,

    Regarding the: https://github.com/mogilvie/EncryptBundle/issues/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.

    enhancement 
    opened by Volmarg 9
  • Symfony 4 - Encrypted and decrypted key is the same.

    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=

    bug help wanted 
    opened by Volmarg 7
  • Cannot instantiate interface SpecShaper\EncryptBundle\Encryptors\EncryptorInterface

    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

    opened by Jeordy 6
  • `is_disabled` and `encrypt_key` empty

    `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).

    opened by Volmarg 4
  • Symfony 4 - encrypt:genkey/encrypt_key

    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

    bug help wanted 
    opened by Volmarg 4
  • postFlush: Decode whole entities again

    postFlush: Decode whole entities again

    I had a problem when saving entities with a collection of other entities that contain encrypted fields. After flushing the entity the fields of the entities in the collection where not decrypted again in postFlush.

    So I made this change that simplifies the code and just calls the decrypt function processFields on the flushed entities again, which solved the problem for me.

    opened by maps82 3
  • Add a command to encrypt existing database

    Add a command to encrypt existing database

    I have copy code from listeners existing in this repository

    Maybe it can be usefull to refactor into services but I don't have the time

    I suggest this PR anyways

    Have a good day

    opened by FournyP 2
  • fix deprecation notices - Added explicit @return annotation

    fix deprecation notices - Added explicit @return annotation

    Fixed deprecation notice using symfony 5.4.2:

    • [x] 1x: Method "Symfony\Component\Console\Command\Command::execute()" might add "int" as a native return type declaration in the future. Do the same in child class "SpecShaper\EncryptBundle\Command\GenKeyCommand" now to avoid errors or add an explicit @return annotation to suppress this message.
    opened by raulpuente 2
  • Installation error

    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"
    opened by kr4ft3r 2
  • [Question] What do You need `DoctrineEncryptSubscriber::postLoad` for?

    [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?

    question 
    opened by Volmarg 2
  • Encrypted values get re-encrypted

    Encrypted values get re-encrypted

    So, it seems and most definitely is that You the bundle simply encrypts the value no matter if it was encrypted or not. I found and issue when I did this:

    • Project A:
      • contain some data,
      • data is encrypted before sending to api,
      • api call is done to project B,
    • Project B
      • runs into timeout (clearly php_ini stuff, but that's not the point),
      • partially imported data is not working

    So the timeout happens because the B encrypts tones of the data again. Imported data is not working since the already encrypted data get's re-encrypted.

    opened by Volmarg 2
  • Simplify doctrine flow

    Simplify doctrine flow

    I had issue with $em->refresh($entity);, the value was not decrypted again. Here is a simpler approach I think, we tell Doctrine the original value was the decrypted one. So we don't have to deal with many checks.

    opened by 4rthem 1
  • Manually decrypt value

    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?

    enhancement help wanted 
    opened by labrador-gibraltar 2
  • [Malfunction] `onEncryptionLoadKey` purpose gets violated by `postLoad`

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

    opened by Volmarg 4
  • Optimize large set of processed data for API calls

    Optimize large set of processed data for API calls

    I was trying to localize the bottleneck in my API calls. That's the one

    Without that:

    • Start: 17:57:40
    • End: 17:59:28
    • Duration: 1min 48s
    • Result: not even half gets transferred and each call gets slower.... and slower... had eventually timeouts

    With that:

    • Start: 18:05:29
    • End: 18:06:06
    • Duration: 37s
    • Result: done, everything works
    opened by Volmarg 1
  • Add non-annotation mapping

    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)

    enhancement help wanted 
    opened by garak 1
Releases(v3.0.2)
Owner
Mark Ogilvie
Mark Ogilvie
BitTorrent library for encoding and decoding torrents in PHP language.

PHPBitTorrentLib BitTorrent library for PHP. This library been tested and works on PHP 7.4+, it originally was just a project to handle the process of

Lee Howarth 3 Dec 19, 2022
PHP Class Encoding featuring popular Encoding::toUTF8() function --formerly known as forceUTF8()-- that fixes mixed encoded strings.

forceutf8 PHP Class Encoding featuring popular \ForceUTF8\Encoding::toUTF8() function --formerly known as forceUTF8()-- that fixes mixed encoded strin

Sebastián Grignoli 1.6k Dec 22, 2022
Pageon Doctrine Data Grid Bundle

Pageon Doctrine Data Grid Bundle A bundle that wraps around the knp paginator bundle and doctrine to generate a data grid from your entity Documentati

null 1 Dec 14, 2021
Symfony Bundle to create HTML tables with bootstrap-table for Doctrine Entities.

HelloBootstrapTableBundle This Bundle provides simple bootstrap-table configuration for your Doctrine Entities. Used bootstrap-table version 1.18.3. I

Sebastian B 7 Nov 3, 2022
urldecode'ing of all HTTP parameters in Lumen project

Request UrlDecode Description This package is intended to perform urldecode of all HTTP parameters in Lumen project. Lumen for some reason does not do

null 1 Dec 13, 2021
Preferences are configuration variables that are user-managed for which we cannot rely upon container parameters or environment variables.

Preferences Preferences are configuration variables that are meant to be user managed for which we cannot rely upon container parameters or environmen

Makina Corpus 1 Feb 7, 2022
A PocketMine-MP plugin that allows you to comfortably register events using a new piece of PHP 8 power — attributes.

AdvancedEvents This is a PocketMine-MP plugin that allows you to comfortably register events using a new piece of PHP 8 power — attributes. Inspired b

JuraSciix 7 Dec 5, 2022
This package provides a set of factories to be used with containers using the PSR-11 standard for an easy Doctrine integration in a project

psr-container-doctrine: Doctrine Factories for PSR-11 Containers Doctrine factories for PSR-11 containers. This package provides a set of factories to

Roave, LLC 84 Dec 14, 2022
An un-offical API wrapper for logsnag.com to get notifications and track your project events

An un-offical API wrapper for logsnag.com to get notifications and track your project events

David Oti 3 Oct 15, 2022
Laravel Pipeline with DB transaction support, events and additional methods

Laravel Enhanced Pipeline Laravel Pipeline with DB transaction support, events and additional methods #StandWithUkraine Installation Install the packa

Michael Rubél 33 Dec 3, 2022
A Pocketmine-MP plugin to add King Of The Hill events to your server.

KOTH KOTH is an event popular on HCF and Faction servers. This plugin lets you add this minigame to you server. Support For questions, please join the

ItsMax123 9 Sep 17, 2022
Paddle.com API integration for Laravel with support for webhooks/events

Laravel Paddle This package provides an integration with Paddle.com for Laravel. Read the blogpost about the introduction of the package! Features Sup

Protone Media 179 Dec 16, 2022
Paddle.com API integration for Laravel with support for webhooks/events

Laravel Paddle This package provides an integration with Paddle.com for Laravel. Read the blogpost about the introduction of the package! Features Sup

Protone Media 180 Jan 1, 2023
Generates a static website of metal music events in Leipzig

About EN: This projects generates a static website of metal music events in Leipzig (Ger). DE: Dieses Projekt erstellt einen Webkalender zu diversen M

null 3 Dec 15, 2022
OwnagePE Staff Events Core

An events plugin created for discord.gg/ownage enabling staff members to host small scale events within the respective server.

null 2 Aug 9, 2022
A XOOPS module for handling events, including online registrations.

wgEvents A XOOPS module for handling events, including online registrations. Support If you like the wgEvents module and thanks to the long process fo

XOOPS 2.5.x Modules 6 Dec 15, 2022
Laravel Plans is a package for SaaS apps that need management over plans, features, subscriptions, events for plans or limited, countable features.

Laravel Plans Laravel Plans is a package for SaaS apps that need management over plans, features, subscriptions, events for plans or limited, countabl

ángel 2 Oct 2, 2022
An advanced plugin to manage events when the player enters the server!

⭐ • Better Join Version Status Date 1.0.0 stable-dev 12/10/2022 ?? • General: Plugin Introduction: This is an advanced player input management plugin

HenryDM 3 Nov 2, 2022
JSON schema models and generated code to validate and handle various data in PocketMine-MP

DataModels JSON schema models and generated code to validate and handle various data in PocketMine-MP This library uses php-json-schema-model-generato

PMMP 2 Nov 9, 2022