nowo-tech/doctrine-encrypt-bundle

Encrypt Doctrine entity fields at rest with Halite, Defuse, or MySQL-compatible MysqlAes. Symfony 7|8 bundle for field-level encryption, GDPR-friendly, multiple keys, key rotation.

Maintainers

Package info

github.com/nowo-tech/DoctrineEncryptBundle

Documentation

Type:symfony-bundle

pkg:composer/nowo-tech/doctrine-encrypt-bundle

Fund package maintenance!

HecFranco

Statistics

Installs: 1 429

Dependents: 0

Suggesters: 0

Stars: 3

Open Issues: 6

v2.2.2 2026-06-11 12:41 UTC

README

CI Packagist Version Packagist Downloads License PHP Symfony

Symfony bundle to encrypt Doctrine entity fields at rest using Halite or Defuse—audited libraries, no custom crypto. For Symfony 7.4+ and 8 · PHP 8.2+. Suits GDPR and compliance (e.g. Art. 32); supports key rotation and Nowo\AnonymizedBundle for anonymization and erasure.

Found this useful? Install from Packagist · Give it a star on GitHub so more developers can find it.

Table of contents

Quick search terms

Looking for Doctrine encryption, encrypt entity fields, Halite Symfony, Defuse encryption, field-level encryption, encrypt database column, Symfony encrypt attribute, Doctrine Encrypted? You're in the right place.

Features

  • ✅ Encrypt and decrypt entity properties with a single attribute
  • Multiple encryptor configs — e.g. personal_data (Halite) and financial_data (Defuse) in the same app, each with its own key
  • Halite, Defuse, and MysqlAes (MySQL AES_ENCRYPT / AES_DECRYPT compatible) — audited or interoperable crypto; see MYSQL_AES.md
  • ✅ Transparent: encrypt on persist/update, decrypt on load
  • EncryptUtil — programmatic encrypt() / decrypt() with optional config name (default or e.g. financial_data)
  • MaskUtil — mask sensitive values in PHP (e.g. show only last N chars); usable in services
  • Twig filters|decrypt (decrypt in templates; optional config: {{ value|decrypt }} or {{ value|decrypt('financial_data') }}) and |mask (mask for display: {{ value|mask(4) }} or {{ value|decrypt|mask(4) }})
  • ✅ Works with embedded entities and inheritance
  • ✅ Console commands: status, generate secret key, encrypt/decrypt database, rotate keys (backup, decrypt, change keys, re-encrypt with confirmations)
  • Key rotation — one command or manual steps; combinable with Nowo\AnonymizedBundle for GDPR-compliant anonymization and erasure
  • Symfony Flex recipe (register bundle + config; see docs/INSTALLATION.md)
  • ✅ Compatible with Symfony 7.4+ and 8 and Doctrine ORM 2.x and 3.x
  • ✅ Compatible with FrankenPHP (HTTP runtime and optional worker mode; demos default to APP_ENV=dev with Caddyfile.dev, i.e. no PHP worker — see docs/DEMO-FRANKENPHP.md and Installation → FrankenPHP)

Installation

composer require nowo-tech/doctrine-encrypt-bundle

Install from Packagist

With Symfony Flex, the recipe (when enabled) registers the bundle and creates the config file automatically. Without Flex, see docs/INSTALLATION.md for manual steps.

Manual registration in config/bundles.php:

<?php

return [
  // ...
  Nowo\DoctrineEncryptBundle\NowoDoctrineEncryptBundle::class => ['all' => true],
];

Configuration

Create config/packages/nowo_doctrine_encrypt.yaml. You can use one encryptor (legacy) or multiple named configs (recommended).

Multiple configs (recommended)

Use different encryptors and keys per kind of data (e.g. personal vs financial):

nowo_doctrine_encrypt:
  default_config: personal_data  # used when attribute has no config or uses "default"
  configs:
    personal_data:
      encryptor_class: Halite
      secret_directory_path: '%kernel.project_dir%'
    financial_data:
      encryptor_class: Defuse
      secret_directory_path: '%kernel.project_dir%'

Defuse: composer require defuse/php-encryption ^2.1

Key files: one per config, e.g. .Halite.personal_data.key, .Defuse.financial_data.key in the config’s secret_directory_path. Add to .gitignore:

.Halite.key
.Defuse.key
.Halite.*.key
.Defuse.*.key

Generate keys: php bin/console doctrine:encrypt:generate-secret-key (creates missing Halite/Defuse keys for all configs, or pass a config alias). See docs/CONFIGURATION.md and docs/COMMANDS.md.

Single encryptor (one config)

Use one entry under configs (e.g. default):

nowo_doctrine_encrypt:
  default_config: default
  configs:
    default:
      encryptor_class: Halite  # or Defuse
      secret_directory_path: '%kernel.project_dir%'

Key file: .Halite.default.key (or .Defuse.default.key). Full options: docs/CONFIGURATION.md.

Usage

Mark entity properties with the Encrypted attribute. Use no argument (or "default") for the default config, or the config name when using multiple configs:

use Nowo\DoctrineEncryptBundle\Configuration\Encrypted;

#[ORM\Entity]
class User
{
  #[ORM\Column(type: 'string')]
  #[Encrypted]  // or #[Encrypted('default')] — uses default_config
  private ?string $email = null;
}

With multiple configs, pass the config alias per property:

#[ORM\Column(type: 'string')]
#[Encrypted('personal_data')]
private ?string $email = null;

#[ORM\Column(type: 'string')]
#[Encrypted('financial_data')]
private ?string $iban = null;

Values are encrypted on persist/update and decrypted on load. For programmatic use: EncryptUtil (encrypt/decrypt) and MaskUtil (mask for display). In Twig use the |decrypt and |mask filters. See docs/USAGE.md for EncryptUtil, MaskUtil, Twig filters, embedded entities, and inheritance.

Documentation

Additional documentation

Requirements

  • PHP >= 8.2
  • Symfony 7.4+ or 8 (^7.0 || ^8.0; tested on 7.4, 8.0, 8.1). See composer.json.
  • Doctrine ORM ^2.15 || ^3.0
  • paragonie/halite (included); for Defuse: defuse/php-encryption ^2.1
  • ext-sodium recommended for Halite (or sodium_compat)

See docs/INSTALLATION.md and docs/UPGRADING.md for compatibility notes.

Demo

Demos for Symfony 7 and 8 are in demo/symfony7, demo/symfony8. Each runs with FrankenPHP and Caddy (HTTP on port 80 in the container). docker-compose defaults to APP_ENV=dev, so the entrypoint uses Caddyfile.dev (no PHP worker; changes visible on refresh). Worker mode is for a production-style setup — docs/DEMO-FRANKENPHP.md. Default host ports: 8007 (symfony7), 8008 (symfony8) via PORT. Quick start: docs/DEMO.md.

Development

Run tests and QA with Docker: make up && make install && make test (or make test-coverage, make qa). Without Docker: composer install && composer test. See Makefile for all targets.

Tests and coverage

  • Tests: PHPUnit (unit and functional suites)
  • PHP: 96.14%

License

The MIT License (MIT). Please see LICENSE for more information.

Author

Created by Héctor Franco Aceituno at Nowo.tech