From 811050009f70688b60052bb2fdddb31d6accf38f Mon Sep 17 00:00:00 2001 From: Marcel Date: Wed, 8 Apr 2015 14:57:35 +0200 Subject: [PATCH] Updated to Doctrine 2.5 --- .../DoctrineEncryptExtension.php | 4 +- Encryptors/VariableEncryptor.php | 79 +++++++++++++++++++ README.md | 25 +++--- Resources/doc/configuration_reference.md | 2 +- Subscribers/DoctrineEncryptSubscriber.php | 41 ++++------ composer.json | 5 +- 6 files changed, 111 insertions(+), 45 deletions(-) create mode 100644 Encryptors/VariableEncryptor.php diff --git a/DependencyInjection/DoctrineEncryptExtension.php b/DependencyInjection/DoctrineEncryptExtension.php index 7b71f53d..1c3c136a 100644 --- a/DependencyInjection/DoctrineEncryptExtension.php +++ b/DependencyInjection/DoctrineEncryptExtension.php @@ -29,7 +29,7 @@ public function load(array $configs, ContainerBuilder $container) { $services = array('orm' => 'orm-services'); //set supported encryptor classes - $supportedEncryptorClasses = array('aes256' => 'Ambta\DoctrineEncryptBundle\Encryptors\AES256Encryptor'); + $supportedEncryptorClasses = array('variable' => 'Ambta\DoctrineEncryptBundle\Encryptors\VariableEncryptor'); //If no secret key is set, check for framework secret, otherwise throw exception if (empty($config['secret_key'])) { @@ -42,7 +42,7 @@ public function load(array $configs, ContainerBuilder $container) { //If empty encryptor class, use AES256 encryptor if (empty($config['encryptor_class'])) { - $config['encryptor_class'] = $supportedEncryptorClasses['aes256']; + $config['encryptor_class'] = $supportedEncryptorClasses['variable']; } //Set parameters diff --git a/Encryptors/VariableEncryptor.php b/Encryptors/VariableEncryptor.php new file mode 100644 index 00000000..7267d1f8 --- /dev/null +++ b/Encryptors/VariableEncryptor.php @@ -0,0 +1,79 @@ + + */ +class VariableEncryptor implements EncryptorInterface { + + /** + * @var string + */ + private $secretKey; + + /** + * @var string + */ + private $initializationVector; + + /** + * {@inheritdoc} + */ + public function __construct($key) { + $this->secretKey = md5($key); + $this->initializationVector = mcrypt_create_iv( + mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), + MCRYPT_RAND + ); + } + + /** + * {@inheritdoc} + */ + public function encrypt($data) { + + if(is_string($data)) { + return trim(base64_encode(mcrypt_encrypt( + MCRYPT_RIJNDAEL_256, + $this->secretKey, + $data, + MCRYPT_MODE_ECB, + $this->initializationVector + ))) . ""; + } + + /* + * Use ROT13 which is an simple letter substitution cipher with some additions + * Not the safest option but it makes it alot harder for the attacker + * + * Not used, needs improvement or other solution + */ + if(is_integer($data)) { + //Not sure + } + + return $data; + + } + + /** + * {@inheritdoc} + */ + public function decrypt($data) { + + if(is_string($data)) { + return trim(mcrypt_decrypt( + MCRYPT_RIJNDAEL_256, + $this->secretKey, + base64_decode($data), + MCRYPT_MODE_ECB, + $this->initializationVector + )); + } + + return $data; + } +} diff --git a/README.md b/README.md index b98c0db9..f4a1dc29 100644 --- a/README.md +++ b/README.md @@ -55,25 +55,22 @@ I'm using Semantic Versioning like described [here](http://semver.org) The following items will be done in order 1. ~~Review of complete code + fixes/improvements and inline documentation (2.1.1)~~ -2. Add support for the other doctrine relationships (manyToMany, ManyToOne) (2.2) -3. Add "Encryption" (reformating based on key) of integers, data time object (2.3) -4. Recreate documentation (2.4) -5. Create example code (2.4) -6. Create an function to encrypt unencrypted database and vice versa (console command, migration, changed key, etc.) (2.5) -7. Look for a posibility of automatic encryption of query parameters (2.6) -8. Look for a positbility to override findOneBy for automatic encryption of parameters (2.7) -9. Add support to encrypt data by reference to other property as key (Encrypt data specific to user with user key etc.) (2.8) -10. Add "Encryption" (reformating based on key) on all other database types) [Doctrine documentation Types](http://doctrine-dbal.readthedocs.org/en/latest/reference/types.html) (3.0) +2. ~~Add support for the other doctrine relationships (manyToMany, ManyToOne) (2.2)~~ +4. Recreate documentation (2.3) +5. Create example code (2.3) +6. Create an function to encrypt unencrypted database and vice versa (console command, migration, changed key, etc.) (2.4) +7. Look for a posibility of automatic encryption of query parameters (2.5) +8. Look for a posibility to override findOneBy for automatic encryption of parameters (2.6) +9. Add support to encrypt data by reference to other property as key (Encrypt data specific to user with user key etc.) (2.7) +10. Add [Format-preserving encryption](http://en.wikipedia.org/wiki/Format-preserving_encryption) for all data types [Doctrine documentation Types](http://doctrine-dbal.readthedocs.org/en/latest/reference/types.html) (3.0) ####Roadmap #####Goals: -- v2.2 - 17-03-2015 -- v2.3 - 20-03-2015 -- v2.4 - 21-03-2015 -- v2.5 - 31-03-2015 +- v2.3 - 22-04-2015 +- v2.4 - 30-04-2015 +- v2.5 - 31-04-2015 - v2.6 - unknown - v2.7 - unknown -- v2.8 - unknown - v3.0 - 20-05-2015 \ No newline at end of file diff --git a/Resources/doc/configuration_reference.md b/Resources/doc/configuration_reference.md index 52b7ea98..e63cc410 100644 --- a/Resources/doc/configuration_reference.md +++ b/Resources/doc/configuration_reference.md @@ -9,6 +9,6 @@ ambta_doctrine_encrypt: # Store a backup of this key on a secure location, losing this key will mean losing your data! secret_key: ~ # Required # If you want, you can use your own Encryptor. Encryptor must implements EncryptorInterface interface -# Default: Ambta\DoctrineEncryptBundle\Encryptors\AES256Encryptor +# Default: Ambta\DoctrineEncryptBundle\Encryptors\VariableEncryptor encryptor_class: ~ #optional ``` diff --git a/Subscribers/DoctrineEncryptSubscriber.php b/Subscribers/DoctrineEncryptSubscriber.php index 30d7e7ab..41aee019 100644 --- a/Subscribers/DoctrineEncryptSubscriber.php +++ b/Subscribers/DoctrineEncryptSubscriber.php @@ -128,12 +128,17 @@ private function processFields($entity, $isEncryptOperation = true) { $encryptorMethod = $isEncryptOperation ? 'encrypt' : 'decrypt'; //Get the real class, we don't want to use the proxy classes - $realClass = ClassUtils::getClass($entity); + if(strstr(get_class($entity), "Proxies")) { + $realClass = ClassUtils::getClass($entity); + } else { + $realClass = get_class($entity); + } //Get ReflectionClass of our entity $reflectionClass = new ReflectionClass($realClass); $properties = $reflectionClass->getProperties(); + //Foreach property in the reflection class foreach ($properties as $refProperty) { @@ -143,19 +148,6 @@ private function processFields($entity, $isEncryptOperation = true) { */ $methodName = ucfirst($refProperty->getName()); - /** - * Lazy loading, check if the property has an manyToOne relationship. - * if it has look if the set/get exists and recursively call this function based on the entity inside. Only if not empty. - */ - if($this->annReader->getPropertyAnnotation($refProperty, 'Doctrine\ORM\Mapping\ManyToOne')) { - if ($reflectionClass->hasMethod($getter = 'get' . $methodName) && $reflectionClass->hasMethod($setter = 'set' . $methodName)) { - $entity = $entity->$getter(); - if(!empty($entity)) { - $this->processFields($entity, $isEncryptOperation); - } - } - } - /** * If property is an normal value and contains the Encrypt tag, lets encrypt/decrypt that property */ @@ -168,35 +160,36 @@ private function processFields($entity, $isEncryptOperation = true) { $propName = $refProperty->getName(); $entity->$propName = $this->encryptor->$encryptorMethod($refProperty->getValue()); } else { - //If private or protected check if there is an getter/setter for the property, based on the $methodName if ($reflectionClass->hasMethod($getter = 'get' . $methodName) && $reflectionClass->hasMethod($setter = 'set' . $methodName)) { //Get the information (value) of the property - $getInformation = $entity->$getter(); + try { + $getInformation = $entity->$getter(); + } catch(\Exception $e) { + $getInformation = null; + var_dump($e); + } /** * Then decrypt, encrypt the information if not empty, information is an string and the tag is there (decrypt) or not (encrypt). * The will be added at the end of an encrypted string so it is marked as encrypted. Also protects against double encryption/decryption */ if($encryptorMethod == "decrypt") { - if(!is_null($getInformation) and !empty($getInformation) and is_string($getInformation)) { - if(substr($entity->$getter(), -5) == "") { - $currentPropValue = $this->encryptor->$encryptorMethod(substr($entity->$getter(), 0, -5)); + if(!is_null($getInformation) and !empty($getInformation)) { + if(substr($getInformation, -5) == "") { + $currentPropValue = $this->encryptor->decrypt(substr($getInformation, 0, -5)); $entity->$setter($currentPropValue); } } } else { - if(!is_null($getInformation) and !empty($getInformation) and is_string($getInformation)) { + if(!is_null($getInformation) and !empty($getInformation)) { if(substr($entity->$getter(), -5) != "") { - $currentPropValue = $this->encryptor->$encryptorMethod($entity->$getter()) . ""; + $currentPropValue = $this->encryptor->encrypt($entity->$getter()); $entity->$setter($currentPropValue); } } } - - } else { - throw new \RuntimeException(sprintf("Property %s isn't public and doesn't has getter/setter")); } } } diff --git a/composer.json b/composer.json index acedfb03..fc3f773f 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,7 @@ "require": { "php": ">=5.3.2", "symfony/framework-bundle": ">=2.0", + "doctrine/orm": ">=2.5", "ext-mcrypt": "*" }, "autoload": { @@ -14,10 +15,6 @@ }, "target-dir": "Ambta/DoctrineEncryptBundle", "authors": [ - { - "name": "Victor Melnik", - "email": "melnikvictorl@gmail.com" - }, { "name": "Marcel van Nuil", "email": "marcel@ambta.com"