netresearch/nr-vault

Secure secrets management for TYPO3 with envelope encryption, access control, and audit logging

Installs: 0

Dependents: 1

Suggesters: 0

Security: 0

Stars: 1

Watchers: 1

Forks: 0

Open Issues: 2

Type:typo3-cms-extension

pkg:composer/netresearch/nr-vault


README

CI codecov Security OpenSSF Scorecard OpenSSF Best Practices TYPO3 PHP PHPStan License Latest Release Contributor Covenant SLSA 3

Enterprise-grade secret management without enterprise-grade complexity.

The Problem

Your TYPO3 site integrates with Stripe, SendGrid, Google Maps, and a dozen other services. Where are those API keys right now?

Probably in plain text in LocalConfiguration.php, unencrypted in a database field, or hardcoded somewhere accessible to every backend user.

If your database leaks, your secrets leak. If you need to rotate a compromised key, you're editing config files and redeploying.

How Secrets Are Typically Stored

Method Security Operational Reality
External Services (HashiCorp Vault, AWS SM) ⭐⭐⭐⭐⭐ Infrastructure cost, network access, auth to service
Environment Variables ⭐⭐⭐ Deployment/host access required, restart to change, no rotation UI, no audit trail
Files outside webroot ⭐⭐⭐ Deployment/host access required, hard to rotate, no management interface
nr-vault (encrypted DB) ⭐⭐⭐⭐ Runtime manageable via TYPO3 backend, rotate anytime, full audit trail
Plain text in config/DB ❌ No protection

Why nr-vault?

All "more secure" methods require either external infrastructure, deployment pipelines, or server access. And they all lack a management UI and audit trail.

Challenge Env Vars / Files nr-vault
Rotate a compromised API key Call DevOps, redeploy, restart Click in backend, done
See who accessed a secret Check deploy logs (if any) Full audit log with timestamps
Emergency credential revocation Wait for deployment pipeline Immediate via backend module
Non-technical editor updates SMTP password Create support ticket Self-service in backend
Compliance audit: prove access history Manually correlate logs Export tamper-evident audit trail

Solution

nr-vault provides:

  • Envelope encryption with AES-256-GCM via libsodium
  • Master key management (file, environment variable, or derived)
  • Per-secret access control via backend user groups with context scoping
  • Audit logging of all secret access with tamper-evident hash chain
  • Key rotation support for both secrets and master key
  • TCA integration via custom vaultSecret field type
  • Vault HTTP Client - make authenticated API calls without exposing secrets
  • CLI commands for DevOps automation
  • Pluggable adapter architecture (external vault adapters planned for future releases)

Architecture

flowchart TB
    subgraph TYPO3["TYPO3 Backend"]
        subgraph Entry["Entry Points"]
            TCA["TCA Field<br/>(vaultSecret)"]
            Backend["Backend Module<br/>(Secrets Manager)"]
            CLI["CLI Commands"]
        end

        TCA & Backend & CLI --> VaultService

        subgraph VaultService["VaultService"]
            API["store() | retrieve() | rotate() | delete() | list() | http()"]
        end

        VaultService --> AccessControl["AccessControl<br/>Service"]
        VaultService --> Encryption["EncryptionService"]
        VaultService --> Audit["AuditLogService"]

        Encryption --> Adapters

        subgraph Adapters["Vault Adapters"]
            Local["LocalDatabase<br/>(DEFAULT)"]
            Future["Future: HashiCorp,<br/>AWS, Azure"]
        end
    end
Loading

Encryption Model

Uses envelope encryption (same pattern as AWS KMS, Google Cloud KMS):

flowchart TB
    MK["🔐 Master Key<br/>(stored outside database)"]
    DEK["🔑 Data Encryption Key (DEK)<br/>(unique per secret)"]
    Secret["📄 Secret Value<br/>(API key, password, token)"]

    MK -->|encrypts| DEK
    DEK -->|encrypts| Secret
Loading

Benefits:

  • Master key rotation only requires re-encrypting DEKs (fast)
  • Each secret has unique encryption
  • Compromise of one secret doesn't expose others

Quick Start

Store and Retrieve Secrets

use Netresearch\NrVault\Service\VaultServiceInterface;

class MyService
{
    public function __construct(
        private readonly VaultServiceInterface $vault,
    ) {}

    public function storeApiKey(string $provider, string $apiKey): void
    {
        $this->vault->store(
            identifier: "my_extension_{$provider}_api_key",
            secret: $apiKey,
            options: [
                'owner' => $GLOBALS['BE_USER']->user['uid'],
                'groups' => [1, 2],  // Admin, Editor groups
                'context' => 'payment',  // Permission scoping
                'expiresAt' => time() + 86400 * 90,  // 90 days
            ]
        );
    }

    public function getApiKey(string $provider): ?string
    {
        return $this->vault->retrieve("my_extension_{$provider}_api_key");
    }
}

Vault HTTP Client

Make authenticated API calls without exposing secrets to your code:

use Netresearch\NrVault\Http\SecretPlacement;
use Netresearch\NrVault\Http\VaultHttpClientInterface;

class PaymentService
{
    public function __construct(
        private readonly VaultHttpClientInterface $httpClient,
    ) {}

    public function chargeCustomer(array $payload): array
    {
        // Secret is injected by vault - never visible to this code
        $response = $this->httpClient->post(
            'https://api.stripe.com/v1/charges',
            [
                'auth_secret' => 'stripe_api_key',
                'placement' => SecretPlacement::Bearer,
                'json' => $payload,
            ],
        );

        return json_decode($response->getBody()->getContents(), true);
    }
}

Secret placement options: Bearer, BasicAuth, Header, QueryParam, BodyField, ApiKey, OAuth2.

TCA Integration

'api_key' => [
    'label' => 'API Key',
    'config' => [
        'type' => 'input',
        'renderType' => 'vaultSecret',
        'size' => 30,
    ],
],

CLI Commands

# Initialize vault (create master key)
vendor/bin/typo3 vault:init

# List secrets (respects access control)
vendor/bin/typo3 vault:list

# Rotate a secret
vendor/bin/typo3 vault:rotate my_secret_id --reason="Scheduled rotation"

# Rotate master key (re-encrypts all DEKs)
vendor/bin/typo3 vault:rotate-master-key --new-key=/path/to/new.key --confirm

# View audit log
vendor/bin/typo3 vault:audit --identifier=my_secret_id --days=30

# Export secrets (encrypted backup)
vendor/bin/typo3 vault:export --output=secrets.enc

Requirements

  • TYPO3: v14.0+
  • PHP: ^8.5
  • Extensions: ext-sodium (bundled with PHP)
  • CPU: AES-NI support recommended (XChaCha20-Poly1305 fallback available)

Documentation

Full documentation is available in the Documentation/ folder and can be rendered with the TYPO3 documentation tools.

Render locally

docker run --rm -v $(pwd):/project ghcr.io/typo3-documentation/render-guides:latest --progress Documentation
# Open Documentation-GENERATED-temp/Index.html

Planning documents

Internal development documents are available in docs/:

Feature Comparison

Feature nr-vault Drupal Key Laravel Secrets Symfony Secrets
Envelope encryption Yes No No No
Per-secret DEKs Yes No No No
External vault support Planned Pluggable Limited HashiCorp
Access control BE groups + context By key N/A N/A
Audit logging Full + hash chain Limited None None
TCA/Form integration Native Form API N/A N/A
Key rotation CLI Yes Manual Yes Yes
HTTP client Yes No No No
OAuth auto-refresh Yes No No No

Roadmap

  • Phase 1-5: Core functionality (current focus)
  • Phase 6: External adapters (HashiCorp, AWS, Azure) + Optional Rust FFI for zero-PHP-exposure
  • Phase 7: Service Registry - abstract away both credentials AND endpoints

Installation

composer require netresearch/nr-vault

Or in DDEV:

ddev start
ddev install-v14
ddev vault-init

License

GPL-2.0-or-later

[n] Developed by Netresearch DTT GmbH - Enterprise TYPO3 Solutions