├── .github └── workflows │ └── tests.yaml ├── .gitignore ├── .php-cs-fixer.php ├── Annotations ├── AnnotationResolver.php ├── ParamDecryptor.php └── ParamEncryptor.php ├── DependencyInjection ├── Compiler │ └── LoadAnnotationService.php ├── Configuration.php └── NzoEncryptorExtension.php ├── Encryptor └── Encryptor.php ├── LICENSE ├── NzoUrlEncryptorBundle.php ├── README.md ├── Resources └── config │ └── services.yaml ├── Tests ├── Annotation │ ├── AnnotationResolverTest.php │ └── Fixtures │ │ └── DummyController.php └── Encryptor │ └── NzoEncryptorExtensionTest.php ├── Twig └── EncryptorExtension.php ├── composer.json ├── phpstan.neon ├── phpunit.xml.dist └── rector.php /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | pull_request: ~ 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | run: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | php-version: 16 | - "7.1.3" 17 | - "7.2" 18 | - "7.4" 19 | - "8.0" 20 | - "8.0" 21 | - "8.1" 22 | - "8.2" 23 | - "8.3" 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v3 27 | 28 | - name: Setup PHP 29 | uses: shivammathur/setup-php@v2 30 | with: 31 | php-version: ${{ matrix.php-version }} 32 | 33 | - name: Install dependencies 34 | run: composer install 35 | 36 | - name: Run PHPUnit tests 37 | run: | 38 | ./vendor/bin/phpunit 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /phpunit.xml 2 | /.phpunit.result.cache 3 | /composer.lock 4 | /vendor 5 | /.idea 6 | /docker-compose.yml 7 | /Makefile 8 | -------------------------------------------------------------------------------- /.php-cs-fixer.php: -------------------------------------------------------------------------------- 1 | in(__DIR__) 5 | ->exclude([ 6 | '.github/', 7 | 'vendor/', 8 | ]) 9 | ; 10 | 11 | $config = (new PhpCsFixer\Config()) 12 | ->setRiskyAllowed(true) 13 | ->setRules([ 14 | '@PSR12' => true, 15 | '@Symfony' => true, 16 | 'array_syntax' => ['syntax' => 'short'], 17 | 'combine_consecutive_unsets' => true, 18 | 'heredoc_to_nowdoc' => true, 19 | 'no_extra_blank_lines' => ['tokens' => ['break', 'continue', 'extra', 'return', 'throw', 'use', 'parenthesis_brace_block', 'square_brace_block', 'curly_brace_block']], 20 | 'no_unreachable_default_argument_value' => true, 21 | 'no_useless_else' => true, 22 | 'no_useless_return' => true, 23 | 'ordered_class_elements' => true, 24 | 'ordered_imports' => true, 25 | 'php_unit_strict' => true, 26 | 'phpdoc_order' => true, 27 | 'strict_comparison' => true, 28 | 'strict_param' => true, 29 | 'concat_space' => ['spacing' => 'one'], 30 | ]) 31 | ->setFinder($finder); 32 | 33 | return $config; 34 | -------------------------------------------------------------------------------- /Annotations/AnnotationResolver.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Nzo\UrlEncryptorBundle\Annotations; 13 | 14 | use Doctrine\Common\Annotations\AnnotationReader; 15 | use Nzo\UrlEncryptorBundle\Encryptor\Encryptor; 16 | use Symfony\Component\HttpKernel\Event\ControllerEvent; 17 | 18 | class AnnotationResolver 19 | { 20 | private $encryptor; 21 | private $reader; 22 | 23 | public function __construct(Encryptor $encryptor, ?AnnotationReader $reader = null) 24 | { 25 | $this->encryptor = $encryptor; 26 | $this->reader = $reader; 27 | } 28 | 29 | public function onKernelController(ControllerEvent $event) 30 | { 31 | if (is_array($controller = $event->getController())) { 32 | $objectController = new \ReflectionObject($controller[0]); 33 | $method = $objectController->getMethod($controller[1]); 34 | } elseif (is_object($controller) && method_exists($controller, '__invoke')) { 35 | $objectController = new \ReflectionObject($controller); 36 | $method = $objectController->getMethod('__invoke'); 37 | } else { 38 | return; 39 | } 40 | 41 | // Handle PHP8 Attributes 42 | if (class_exists('ReflectionAttribute')) { 43 | if ($this->hasAnnotation($method) && !$this->reader instanceof AnnotationReader) { 44 | throw new \InvalidArgumentException('NzoEncryptor: Annotation service not loaded, PHP Attributes should be used instead.'); 45 | } 46 | $annotations = $this->getAnnotation($method); 47 | } elseif ($this->reader instanceof AnnotationReader) { // Handle Annotation only without Attributes 48 | $annotations = $this->reader->getMethodAnnotations($method); 49 | } else { 50 | throw new \InvalidArgumentException('NzoEncryptor: Doctrine Annotation package must be installed, doctrine/annotations.'); 51 | } 52 | 53 | foreach ($annotations as $configuration) { 54 | // handle php8 attribute 55 | if (class_exists('ReflectionAttribute')) { 56 | $configuration = $this->handleReflectionAttribute($configuration); 57 | } 58 | 59 | if ($configuration instanceof ParamEncryptor) { 60 | if (null !== $configuration->getParams()) { 61 | $request = $event->getRequest(); 62 | foreach ($configuration->getParams() as $param) { 63 | if ($request->attributes->has($param)) { 64 | $decrypted = $this->encryptor->encrypt($request->attributes->get($param)); 65 | $request->attributes->set($param, $decrypted); 66 | } elseif ($request->request->has($param)) { 67 | $decrypted = $this->encryptor->encrypt($request->request->get($param)); 68 | $request->request->set($param, $decrypted); 69 | } 70 | } 71 | } 72 | } elseif ($configuration instanceof ParamDecryptor) { 73 | if (null !== $configuration->getParams()) { 74 | $request = $event->getRequest(); 75 | foreach ($configuration->getParams() as $param) { 76 | if ($request->attributes->has($param)) { 77 | $decrypted = $this->encryptor->decrypt($request->attributes->get($param)); 78 | $request->attributes->set($param, $decrypted); 79 | } elseif ($request->request->has($param)) { 80 | $decrypted = $this->encryptor->decrypt($request->request->get($param)); 81 | $request->request->set($param, $decrypted); 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | private function handleReflectionAttribute($configuration) 90 | { 91 | if ($configuration instanceof \ReflectionAttribute 92 | && \in_array($configuration->getName(), [ParamEncryptor::class, ParamDecryptor::class], true)) { 93 | $class = $configuration->getName(); 94 | $arguments = $configuration->getArguments(); 95 | $params = \is_array($arguments) && [] !== $arguments && \is_array($arguments[0]) ? $arguments[0] : []; 96 | 97 | return new $class($params); 98 | } 99 | 100 | return $configuration; 101 | } 102 | 103 | /** 104 | * @return array|mixed 105 | */ 106 | private function getAnnotation($method) 107 | { 108 | return $this->reader instanceof AnnotationReader && !empty($this->reader->getMethodAnnotations($method)) 109 | ? $this->reader->getMethodAnnotations($method) 110 | : $method->getAttributes(); 111 | } 112 | 113 | /** 114 | * @param \ReflectionMethod $method 115 | * 116 | * @return bool 117 | */ 118 | private function hasAnnotation($method) 119 | { 120 | $docComment = $method->getDocComment(); 121 | 122 | return false !== $docComment && (false !== strpos($docComment, '@ParamEncryptor') || false !== strpos($docComment, '@ParamDecryptor')); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Annotations/ParamDecryptor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Nzo\UrlEncryptorBundle\Annotations; 13 | 14 | /** 15 | * @Annotation 16 | */ 17 | #[\Attribute] 18 | class ParamDecryptor 19 | { 20 | /** @var array */ 21 | private $params; 22 | 23 | public function __construct(array $params = []) 24 | { 25 | $this->params = $params; 26 | } 27 | 28 | public function getParams(): array 29 | { 30 | if (isset($this->params['value']) && \is_array($this->params['value'])) { 31 | return $this->params['value']; 32 | } 33 | 34 | return $this->params; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Annotations/ParamEncryptor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Nzo\UrlEncryptorBundle\Annotations; 13 | 14 | /** 15 | * @Annotation 16 | */ 17 | #[\Attribute] 18 | class ParamEncryptor 19 | { 20 | /** @var array */ 21 | private $params; 22 | 23 | public function __construct(array $params = []) 24 | { 25 | $this->params = $params; 26 | } 27 | 28 | public function getParams(): array 29 | { 30 | if (isset($this->params['value']) && \is_array($this->params['value'])) { 31 | return $this->params['value']; 32 | } 33 | 34 | return $this->params; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /DependencyInjection/Compiler/LoadAnnotationService.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Nzo\UrlEncryptorBundle\DependencyInjection\Compiler; 13 | 14 | use Nzo\UrlEncryptorBundle\Annotations\AnnotationResolver; 15 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 16 | use Symfony\Component\DependencyInjection\ContainerBuilder; 17 | 18 | class LoadAnnotationService implements CompilerPassInterface 19 | { 20 | public function process(ContainerBuilder $container): void 21 | { 22 | $container 23 | ->register('nzo.annotation_resolver', AnnotationResolver::class) 24 | ->addArgument($container->getDefinition('nzo_encryptor')) 25 | ->addArgument($container->has('annotations.reader') ? $container->getDefinition('annotations.reader') : null) 26 | ->addTag('kernel.event_listener', ['event' => 'kernel.controller', 'method' => 'onKernelController']) 27 | ; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Nzo\UrlEncryptorBundle\DependencyInjection; 13 | 14 | use Symfony\Component\Config\Definition\Builder\TreeBuilder; 15 | use Symfony\Component\Config\Definition\ConfigurationInterface; 16 | 17 | class Configuration implements ConfigurationInterface 18 | { 19 | public function getConfigTreeBuilder(): TreeBuilder 20 | { 21 | $treeBuilder = new TreeBuilder('nzo_encryptor'); 22 | 23 | $treeBuilder 24 | ->getRootNode() 25 | ->children() 26 | ->scalarNode('secret_key') 27 | ->isRequired() 28 | ->cannotBeEmpty() 29 | ->end() 30 | ->scalarNode('secret_iv') 31 | ->defaultValue('') 32 | ->end() 33 | ->scalarNode('cipher_algorithm') 34 | ->defaultValue('aes-256-ctr') 35 | ->end() 36 | ->booleanNode('base64_encode') 37 | ->defaultValue(true) 38 | ->end() 39 | ->booleanNode('format_base64_output') 40 | ->defaultValue(true) 41 | ->end() 42 | ->booleanNode('random_pseudo_bytes') 43 | ->defaultValue(true) 44 | ->end() 45 | ->end(); 46 | 47 | return $treeBuilder; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /DependencyInjection/NzoEncryptorExtension.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Nzo\UrlEncryptorBundle\DependencyInjection; 13 | 14 | use Symfony\Component\Config\FileLocator; 15 | use Symfony\Component\DependencyInjection\ContainerBuilder; 16 | use Symfony\Component\DependencyInjection\Extension\Extension; 17 | use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; 18 | 19 | class NzoEncryptorExtension extends Extension 20 | { 21 | private const MAX_LENGTH = 100; 22 | 23 | public function load(array $configs, ContainerBuilder $container): void 24 | { 25 | $configuration = new Configuration(); 26 | $config = $this->processConfiguration($configuration, $configs); 27 | 28 | $cipherAlgorithm = $config['cipher_algorithm']; 29 | if (!\in_array($cipherAlgorithm, openssl_get_cipher_methods(true), true)) { 30 | throw new \InvalidArgumentException("NzoEncryptor: Unknown cipher algorithm {$cipherAlgorithm}"); 31 | } 32 | 33 | if (false === (bool) $config['random_pseudo_bytes'] && empty($config['secret_iv'])) { 34 | throw new \InvalidArgumentException("NzoEncryptor: 'secret_iv' cannot be empty when 'random_pseudo_bytes' is set to FALSE !"); 35 | } 36 | 37 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); 38 | $loader->load('services.yaml'); 39 | 40 | $container->setParameter('nzo_encryptor.secret_key', $this->cleanKey($config['secret_key'])); 41 | $container->setParameter('nzo_encryptor.secret_iv', $this->cleanKey($config['secret_iv'])); 42 | $container->setParameter('nzo_encryptor.cipher_algorithm', $cipherAlgorithm); 43 | $container->setParameter('nzo_encryptor.base64_encode', (bool) $config['base64_encode']); 44 | $container->setParameter('nzo_encryptor.format_base64_output', (bool) $config['format_base64_output']); 45 | $container->setParameter('nzo_encryptor.random_pseudo_bytes', (bool) $config['random_pseudo_bytes']); 46 | } 47 | 48 | private function cleanKey(?string $key = null): string 49 | { 50 | if (null === $key || '' === $key || '0' === $key) { 51 | return ''; 52 | } 53 | 54 | $key = trim($key); 55 | if (strlen($key) > self::MAX_LENGTH) { 56 | $key = substr($key, 0, self::MAX_LENGTH); 57 | } 58 | 59 | return $key; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Encryptor/Encryptor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Nzo\UrlEncryptorBundle\Encryptor; 13 | 14 | class Encryptor 15 | { 16 | private const HASH_ALGORITHM = 'sha256'; 17 | 18 | private $secretKey; 19 | private $cipherAlgorithm; 20 | private $base64Encode; 21 | private $formatBase64Output; 22 | private $randomPseudoBytes; 23 | private $iv; 24 | 25 | public function __construct( 26 | string $secretKey, 27 | string $cipherAlgorithm, 28 | bool $base64Encode, 29 | bool $formatBase64Output, 30 | bool $randomPseudoBytes 31 | ) { 32 | $this->secretKey = $secretKey; 33 | $this->cipherAlgorithm = $cipherAlgorithm; 34 | $this->base64Encode = $base64Encode; 35 | $this->formatBase64Output = $formatBase64Output; 36 | $this->randomPseudoBytes = $randomPseudoBytes; 37 | } 38 | 39 | /** 40 | * @param string $secretIv 41 | */ 42 | public function setSecretIv($secretIv) 43 | { 44 | $ivLength = openssl_cipher_iv_length($this->cipherAlgorithm); 45 | $secretIv = $this->randomPseudoBytes ? openssl_random_pseudo_bytes($ivLength) : $secretIv; 46 | 47 | $this->iv = substr( 48 | hash_hmac(self::HASH_ALGORITHM, $secretIv, $this->secretKey, true), 49 | 0, 50 | $ivLength 51 | ); 52 | } 53 | 54 | /** 55 | * @param string $plainText 56 | * 57 | * @return string 58 | */ 59 | public function encrypt($plainText) 60 | { 61 | $encrypted = openssl_encrypt($plainText, $this->cipherAlgorithm, $this->secretKey, OPENSSL_RAW_DATA, $this->iv); 62 | $encrypted = $this->iv . $encrypted; 63 | 64 | return $this->base64Encode ? $this->base64Encode($encrypted) : $encrypted; 65 | } 66 | 67 | /** 68 | * @param string $encrypted 69 | * 70 | * @return string 71 | */ 72 | public function decrypt($encrypted) 73 | { 74 | $ivLength = openssl_cipher_iv_length($this->cipherAlgorithm); 75 | $encrypted = $this->base64Encode ? $this->base64Decode($encrypted) : $encrypted; 76 | $iv = substr($encrypted, 0, $ivLength); 77 | $raw = substr($encrypted, $ivLength); 78 | 79 | $decrypted = openssl_decrypt( 80 | $raw, 81 | $this->cipherAlgorithm, 82 | $this->secretKey, 83 | OPENSSL_RAW_DATA, 84 | $iv 85 | ); 86 | 87 | return trim($decrypted); 88 | } 89 | 90 | /** 91 | * @param string $data 92 | * 93 | * @return string 94 | */ 95 | private function base64Encode($data) 96 | { 97 | if ($this->formatBase64Output) { 98 | return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); 99 | } 100 | 101 | return base64_encode($data); 102 | } 103 | 104 | /** 105 | * @param string $data 106 | * 107 | * @return string 108 | */ 109 | private function base64Decode($data) 110 | { 111 | if ($this->formatBase64Output) { 112 | return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT), true); 113 | } 114 | 115 | return base64_decode($data, true); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Ala Eddine khefifi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /NzoUrlEncryptorBundle.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Nzo\UrlEncryptorBundle; 13 | 14 | use Nzo\UrlEncryptorBundle\DependencyInjection\Compiler\LoadAnnotationService; 15 | use Nzo\UrlEncryptorBundle\DependencyInjection\NzoEncryptorExtension; 16 | use Symfony\Component\DependencyInjection\ContainerBuilder; 17 | use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; 18 | use Symfony\Component\HttpKernel\Bundle\Bundle; 19 | 20 | class NzoUrlEncryptorBundle extends Bundle 21 | { 22 | public function getContainerExtension(): ?ExtensionInterface 23 | { 24 | return new NzoEncryptorExtension(); 25 | } 26 | 27 | public function build(ContainerBuilder $container): void 28 | { 29 | $container->addCompilerPass(new LoadAnnotationService()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NzoUrlEncryptorBundle 2 | ===================== 3 | 4 | [![tests](https://github.com/nayzo/NzoUrlEncryptorBundle/actions/workflows/tests.yaml/badge.svg)](https://github.com/nayzo/NzoUrlEncryptorBundle/actions/workflows/tests.yaml) 5 | [![Total Downloads](https://poser.pugx.org/nzo/url-encryptor-bundle/downloads)](https://packagist.org/packages/nzo/url-encryptor-bundle) 6 | [![Latest Stable Version](https://poser.pugx.org/nzo/url-encryptor-bundle/v/stable)](https://packagist.org/packages/nzo/url-encryptor-bundle) 7 | 8 | The **NzoUrlEncryptorBundle** is a Symfony Bundle used to Encrypt and Decrypt data and variables in the Web application or passed through the ``URL`` to provide more security to the project. 9 | Also it prevent users from reading and modifying sensitive data sent through the ``URL``. 10 | 11 | 12 | #### The Version (^6.0) is compatible with **Symfony >= 4.4** 13 | 14 | 15 | Features include: 16 | 17 | - Url Data & parameters Encryption 18 | - Url Data & parameters Decryption 19 | - Data Encryption & Decryption 20 | - Access from Twig by ease 21 | - Flexible configuration 22 | - Uses OpenSSL extension 23 | 24 | 25 | By default, this bundle use the **aes-256-ctr** algorithm. 26 | 27 | CTR mode (without any additional authentication step) is malleable, which means that it is possible to change the meaning of the ciphertext and if the plaintext is guessable then it could lead to IDOR. 28 | 29 | ##### For more secure output, you must configure the bundle to use a **unique and random IV** (`random_pseudo_bytes: TRUE`) 30 | 31 | 32 | Installation 33 | ------------ 34 | 35 | ### Through Composer: 36 | 37 | Install the bundle: 38 | 39 | ``` 40 | $ composer require nzo/url-encryptor-bundle 41 | ``` 42 | 43 | ### Register the bundle in config/bundles.php (without Flex): 44 | 45 | ``` php 46 | // config/bundles.php 47 | 48 | return [ 49 | // ... 50 | Nzo\UrlEncryptorBundle\NzoUrlEncryptorBundle::class => ['all' => true], 51 | ]; 52 | ``` 53 | 54 | ### Configure the bundle: 55 | 56 | ``` yml 57 | # config/packages/nzo_encryptor.yaml 58 | 59 | nzo_encryptor: 60 | secret_key: Your_Secret_Encryption_Key # Required, max length of 100 characters. 61 | secret_iv: Your_Secret_Iv # Required only if "random_pseudo_bytes" is FALSE. Max length of 100 characters. 62 | cipher_algorithm: # optional, default: 'aes-256-ctr' 63 | base64_encode: # optional, default: TRUE 64 | format_base64_output: # optional, default: TRUE, used only when 'base64_encode' is set to TRUE 65 | random_pseudo_bytes: # optional, default: TRUE (generate a random encrypted text output each time => MORE SECURE !) 66 | ``` 67 | 68 | ##### * To generate the same cypher text each time: `random_pseudo_bytes: FALSE` (Not Secure) 69 | ##### * To generate a different cypher text each time: `random_pseudo_bytes: TRUE` (Secure) 70 | 71 | Usage 72 | ----- 73 | 74 | #### In the twig template: 75 | 76 | Use the twig extensions filters or functions to ``encrypt`` or ``decrypt`` your data: 77 | 78 | ``` html 79 | // Filters: 80 | 81 | # Encryption: 82 | 83 | My link 84 | 85 | {{myVar | nzo_encrypt }} 86 | 87 | # Decryption: 88 | 89 | My link 90 | 91 | {{myVar | nzo_decrypt }} 92 | 93 | 94 | // Functions: 95 | 96 | # Encryption: 97 | 98 | My link 99 | 100 | {{ nzo_encrypt(myVar) }} 101 | 102 | # Decryption: 103 | 104 | My link 105 | 106 | {{ nzo_decrypt(myVar) }} 107 | ``` 108 | 109 | #### In the controller with annotation service: 110 | 111 | Use the annotation service to ``decrypt`` / ``encrypt`` automatically any parameter you want, by using the ``ParamDecryptor`` / ``ParamEncryptor`` annotation service and specifying in it all the parameters to be decrypted/encrypted. 112 | 113 | ```php 114 | use Nzo\UrlEncryptorBundle\Annotations\ParamDecryptor; 115 | use Nzo\UrlEncryptorBundle\Annotations\ParamEncryptor; 116 | 117 | class MyController 118 | { 119 | /** 120 | * @ParamDecryptor({"id", "foo"}) 121 | */ 122 | // OR 123 | #[ParamDecryptor(["id", "foo"])] 124 | public function decryptionAction($id, $foo) 125 | { 126 | // no need to use the decryption service here as the parameters are already decrypted by the annotation service. 127 | //... 128 | } 129 | 130 | /** 131 | * @ParamEncryptor({"id", "foo"}) 132 | */ 133 | // OR 134 | #[ParamEncryptor(["id", "foo"])] 135 | public function encryptionAction($id, $foo) 136 | { 137 | // no need to use the encryption service here as the parameters are already encrypted by the annotation service. 138 | //... 139 | } 140 | } 141 | ``` 142 | 143 | #### With autowiring: 144 | 145 | ```php 146 | use Nzo\UrlEncryptorBundle\Encryptor\Encryptor; 147 | 148 | class MyController 149 | { 150 | private $encryptor; 151 | 152 | public function __construct(Encryptor $encryptor) 153 | { 154 | $this->encryptor = $encryptor; 155 | } 156 | 157 | public function indexAction($data) 158 | { 159 | $encrypted = $this->encryptor->encrypt($data); 160 | 161 | $decrypted = $this->encryptor->decrypt($data); 162 | } 163 | } 164 | ``` 165 | 166 | #### Without autowiring: 167 | 168 | ```php 169 | class MyController 170 | { 171 | public function indexAction($data) 172 | { 173 | $encrypted = $this->get('nzo_encryptor')->encrypt($data); 174 | 175 | $decrypted = $this->get('nzo_encryptor')->decrypt($data); 176 | } 177 | } 178 | ``` 179 | 180 | License 181 | ------- 182 | 183 | This bundle is under the MIT license. See the complete license in the bundle: 184 | 185 | See [LICENSE](https://github.com/nayzo/NzoUrlEncryptorBundle/tree/master/LICENSE) 186 | -------------------------------------------------------------------------------- /Resources/config/services.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | Nzo\UrlEncryptorBundle\Encryptor\Encryptor: "@nzo_encryptor" 4 | 5 | nzo_encryptor: 6 | class: Nzo\UrlEncryptorBundle\Encryptor\Encryptor 7 | arguments: 8 | - "%nzo_encryptor.secret_key%" 9 | - "%nzo_encryptor.cipher_algorithm%" 10 | - "%nzo_encryptor.base64_encode%" 11 | - "%nzo_encryptor.format_base64_output%" 12 | - "%nzo_encryptor.random_pseudo_bytes%" 13 | calls: 14 | - [setSecretIv, ["%nzo_encryptor.secret_iv%"]] 15 | 16 | nzo.twig.encryptor_extension: 17 | class: Nzo\UrlEncryptorBundle\Twig\EncryptorExtension 18 | arguments: 19 | - "@nzo_encryptor" 20 | tags: 21 | - { name: twig.extension } 22 | -------------------------------------------------------------------------------- /Tests/Annotation/AnnotationResolverTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped('At least PHP 8 is needed for this test'); 29 | } 30 | 31 | $encryptor = new Encryptor('foo', 'aes-256-ctr', true, true, true); 32 | $encryptor->setSecretIv('secret'); 33 | 34 | $request = Request::create('/'); 35 | $request->attributes->set('id', $encryptor->encrypt('some_data')); 36 | 37 | $controllerEvent = new ControllerEvent( 38 | $this->createMock(HttpKernelInterface::class), 39 | [new DummyController(), $action], 40 | $request, 41 | 1 42 | ); 43 | 44 | $sut = new AnnotationResolver($encryptor, new AnnotationReader()); 45 | $sut->onKernelController($controllerEvent); 46 | 47 | $this->assertSame('some_data', $request->attributes->get('id')); 48 | } 49 | 50 | public function provideEncryptOnKernelController(): \Iterator 51 | { 52 | yield ['encryptWithAttribute', true]; 53 | yield ['encryptWithAnnotation', false]; 54 | } 55 | 56 | /** 57 | * @dataProvider provideEncryptOnKernelController 58 | */ 59 | public function testEncryptOnKernelController(string $action, bool $needsPhp8): void 60 | { 61 | if ($needsPhp8 && PHP_VERSION_ID < 80000) { 62 | $this->markTestSkipped('At least PHP 8 is needed for this test'); 63 | } 64 | 65 | $encryptor = new Encryptor('foo', 'aes-256-ctr', true, true, true); 66 | $encryptor->setSecretIv('secret'); 67 | 68 | $request = Request::create('/'); 69 | $request->attributes->set('id', 'some_data'); 70 | 71 | $controllerEvent = new ControllerEvent( 72 | $this->createMock(HttpKernelInterface::class), 73 | [new DummyController(), $action], 74 | $request, 75 | 1 76 | ); 77 | 78 | $sut = new AnnotationResolver($encryptor, new AnnotationReader()); 79 | $sut->onKernelController($controllerEvent); 80 | 81 | $this->assertSame($encryptor->encrypt('some_data'), $request->attributes->get('id')); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Tests/Annotation/Fixtures/DummyController.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Nzo\UrlEncryptorBundle\Tests\Encryptor; 13 | 14 | use Nzo\UrlEncryptorBundle\Encryptor\Encryptor; 15 | use PHPUnit\Framework\TestCase; 16 | 17 | class NzoEncryptorExtensionTest extends TestCase 18 | { 19 | public const CIPHER_ALGORITHM = 'aes-256-ctr'; 20 | public const PLAIN_TEXT = 'plain_text'; 21 | public const SECRET_KEY = 'encryptionKeyText'; 22 | public const SECRET_IV = 'encryptionIvText'; 23 | public const BASE64_ENCODE = true; 24 | public const FORMAT_BASE64_OUTPUT = true; 25 | public const RANDOM_PSEUDO_BYTES = false; 26 | 27 | private $encryptor; 28 | 29 | public function setup(): void 30 | { 31 | $this->encryptor = new Encryptor( 32 | self::SECRET_KEY, 33 | self::CIPHER_ALGORITHM, 34 | self::BASE64_ENCODE, 35 | self::FORMAT_BASE64_OUTPUT, 36 | self::RANDOM_PSEUDO_BYTES 37 | ); 38 | 39 | $this->encryptor->setSecretIv(self::SECRET_IV); 40 | } 41 | 42 | public function testEncrypt() 43 | { 44 | $encrypted = $this->encryptor->encrypt(self::PLAIN_TEXT); 45 | $decrypted = $this->encryptor->decrypt($encrypted); 46 | 47 | $this->assertSame(self::PLAIN_TEXT, $decrypted); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Twig/EncryptorExtension.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Nzo\UrlEncryptorBundle\Twig; 13 | 14 | use Nzo\UrlEncryptorBundle\Encryptor\Encryptor; 15 | use Twig\Extension\AbstractExtension; 16 | use Twig\TwigFilter; 17 | use Twig\TwigFunction; 18 | 19 | class EncryptorExtension extends AbstractExtension 20 | { 21 | private $encryptor; 22 | 23 | public function __construct(Encryptor $encryptor) 24 | { 25 | $this->encryptor = $encryptor; 26 | } 27 | 28 | public function getFilters(): array 29 | { 30 | return [ 31 | new TwigFilter('nzo_encrypt', [$this, 'encryptFilter']), 32 | new TwigFilter('nzo_decrypt', [$this, 'decryptFilter']), 33 | ]; 34 | } 35 | 36 | public function getFunctions(): array 37 | { 38 | return [ 39 | new TwigFunction('nzo_encrypt', [$this, 'encryptFunction']), 40 | new TwigFunction('nzo_decrypt', [$this, 'decryptFunction']), 41 | ]; 42 | } 43 | 44 | /** 45 | * @param string $key 46 | * 47 | * @return string 48 | */ 49 | public function encryptFilter($key) 50 | { 51 | return $this->encryptor->encrypt($key); 52 | } 53 | 54 | /** 55 | * @param string $key 56 | * 57 | * @return string 58 | */ 59 | public function decryptFilter($key) 60 | { 61 | return $this->encryptor->decrypt($key); 62 | } 63 | 64 | /** 65 | * @param string $key 66 | * 67 | * @return string 68 | */ 69 | public function encryptFunction($key) 70 | { 71 | return $this->encryptor->encrypt($key); 72 | } 73 | 74 | /** 75 | * @param string $key 76 | * 77 | * @return string 78 | */ 79 | public function decryptFunction($key) 80 | { 81 | return $this->encryptor->decrypt($key); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nzo/url-encryptor-bundle", 3 | "type": "symfony-bundle", 4 | "description": "The NzoUrlEncryptorBundle is a Symfony Bundle used to Encrypt and Decrypt data and variables in the Web application or passed through URL", 5 | "keywords": ["Link", "url", "encryption", "decryption", "encrypt", "decrypt", "id", "data", "variable", "security", "AES"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Ala Eddine Khefifi", 10 | "email": "alakfpro@gmail.com" 11 | } 12 | ], 13 | "autoload": { 14 | "psr-4": { 15 | "Nzo\\UrlEncryptorBundle\\": "" 16 | }, 17 | "exclude-from-classmap": [ 18 | "/Tests/" 19 | ] 20 | }, 21 | "require": { 22 | "php": ">=7.1.3", 23 | "ext-openssl": "*", 24 | "symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0 || ^7.0" 25 | }, 26 | "suggest": { 27 | "doctrine/annotations": "Required for annotation support, compatible with versions ^1.7 || ^2.0", 28 | "twig/twig": "Required for twig nzo_encrypt and nzo_decrypt functions support" 29 | }, 30 | "require-dev": { 31 | "doctrine/annotations": "^1.7 || ^2.0", 32 | "friendsofphp/php-cs-fixer": "^2.2 || ^3.68", 33 | "rector/rector": "*", 34 | "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", 35 | "phpstan/phpstan": "^1.0 || ^2.0" 36 | }, 37 | "config": { 38 | "sort-packages": true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 5 3 | excludePaths: 4 | - vendor/* 5 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ./Tests/ 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /rector.php: -------------------------------------------------------------------------------- 1 | paths([__DIR__]); 13 | $rectorConfig->skip([__DIR__ . '/vendor', __DIR__ . '/.github']); 14 | 15 | $rectorConfig->phpVersion(PhpVersion::PHP_71); 16 | 17 | $rectorConfig->importNames(); 18 | $rectorConfig->importShortClasses(false); 19 | 20 | $rectorConfig->rule(AddParamTypeFromPropertyTypeRector::class); 21 | $rectorConfig->rule(AddParamTypeDeclarationRector::class); 22 | 23 | $rectorConfig->sets([ 24 | SetList::PHP_71, 25 | SetList::CODE_QUALITY, 26 | SymfonySetList::SYMFONY_54, 27 | SymfonySetList::SYMFONY_64, 28 | SymfonySetList::CONFIGS, 29 | SymfonySetList::SYMFONY_CODE_QUALITY, 30 | SymfonySetList::SYMFONY_CONSTRUCTOR_INJECTION, 31 | PHPUnitSetList::PHPUNIT_90, 32 | PHPUnitSetList::PHPUNIT_CODE_QUALITY, 33 | ]); 34 | }; 35 | --------------------------------------------------------------------------------