Files
michaelschiemer/docs/vault-system.md
Michael Schiemer 5050c7d73a docs: consolidate documentation into organized structure
- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
2025-10-05 11:05:04 +02:00

8.6 KiB

Vault System - Secure Secrets Management

Modernes, sicheres Secrets Management System für das Custom PHP Framework.

Features

Libsodium Encryption - Authenticated encryption (AEAD) Database-backed Storage - Transaktionale Sicherheit Audit Logging - Vollständiges Audit Trail aller Operations Metadata Tracking - Access Counts, Last Access, etc. Key Rotation - Hot Key Rotation ohne Downtime CLI Management - Vollständige Console Commands Type Safety - Value Objects für alle Daten Framework Integration - Native DI Container Support

Installation & Setup

1. Migration ausführen

docker exec php php console.php db:migrate

Dies erstellt die drei Vault-Tabellen:

  • vault_secrets - Encrypted secrets storage
  • vault_audit_log - Audit trail
  • vault_encryption_keys - Key version tracking

2. Encryption Key generieren

docker exec php php console.php vault:generate-key

Dies generiert einen kryptographisch sicheren Encryption Key:

VAULT_ENCRYPTION_KEY=aGVsbG8gd29ybGQgdGhpcyBpcyBhIHRlc3Qga2V5IQ==

3. Key in .env speichern

Füge den generierten Key zu deiner .env Datei hinzu:

# Vault Configuration
VAULT_ENCRYPTION_KEY=aGVsbG8gd29ybGQgdGhpcyBpcyBhIHRlc3Qga2V5IQ==

⚠️ WICHTIG: Niemals den Key committen! Die .env Datei ist in .gitignore.

CLI Commands

Secret speichern

# Mit Value als Argument
docker exec php php console.php vault:set database.password "super-secret"

# Value wird interaktiv abgefragt (versteckt)
docker exec php php console.php vault:set database.password

Secret abrufen

docker exec php php console.php vault:get database.password

Secret löschen

docker exec php php console.php vault:delete database.password

Alle Secrets auflisten

docker exec php php console.php vault:list

Zeigt alle Secret Keys mit Metadata:

🔐 Vault Secrets:

• database.password
  Accessed: 15 times, Last: 2024-01-15 14:30:45
• api.stripe.secret_key
  Accessed: 3 times, Last: 2024-01-14 10:20:30

Total: 2 secrets

Audit Log anzeigen

# Letzte 50 Einträge
docker exec php php console.php vault:audit

# Letzte 100 Einträge
docker exec php php console.php vault:audit 100

# Audit Log für spezifischen Secret Key
docker exec php php console.php vault:audit 50 database.password

Key Rotation

docker exec php php console.php vault:rotate-key

⚠️ KRITISCHE OPERATION:

  • Generiert neuen Encryption Key
  • Re-encrypted alle Secrets
  • Gibt neuen Key aus
  • Alte Key aufbewahren bis Rotation verifiziert!

Programmatische Verwendung

In Controller/Services

use App\Framework\Vault\Vault;
use App\Framework\Vault\ValueObjects\SecretKey;
use App\Framework\Vault\ValueObjects\SecretValue;

final readonly class PaymentService
{
    public function __construct(
        private Vault $vault
    ) {}

    public function processPayment(Order $order): PaymentResult
    {
        // Secret aus Vault abrufen
        $apiKey = $this->vault->get(
            SecretKey::from('api.stripe.secret_key')
        );

        // Secret verwenden (reveal() erforderlich!)
        $stripe = new StripeClient($apiKey->reveal());

        return $stripe->charge($order->getTotal());
    }
}

Secret speichern

public function updateApiKey(string $newKey): void
{
    $this->vault->set(
        SecretKey::from('api.stripe.secret_key'),
        new SecretValue($newKey)
    );
}

Secret prüfen

public function hasApiKey(): bool
{
    return $this->vault->has(
        SecretKey::from('api.stripe.secret_key')
    );
}

Metadata abrufen

public function getSecretInfo(string $keyName): array
{
    $metadata = $this->vault->getMetadata(
        SecretKey::from($keyName)
    );

    return [
        'created_at' => $metadata->createdAt,
        'updated_at' => $metadata->updatedAt,
        'access_count' => $metadata->accessCount,
        'last_accessed' => $metadata->lastAccessedAt
    ];
}

Value Objects

SecretKey

Typsicherer Secret Identifier mit Validation:

use App\Framework\Vault\ValueObjects\SecretKey;

// ✅ Gültig
$key = SecretKey::from('database.password');
$key = SecretKey::from('api.stripe.secret-key_2024');

// ❌ Ungültig
$key = SecretKey::from('');  // InvalidArgumentException
$key = SecretKey::from('invalid key!');  // Nur a-zA-Z0-9._- erlaubt

SecretValue

Verhindert accidental Exposure:

use App\Framework\Vault\ValueObjects\SecretValue;

$secret = new SecretValue('my-password');

// ✅ Explizit enthüllen
$password = $secret->reveal();

// ✅ Sichere Checks
if ($secret->isEmpty()) { ... }
$length = $secret->length();

// ✅ Verhindert Logging
echo $secret;  // Ausgabe: [SECRET]

// ✅ Verhindert var_dump Exposure
var_dump($secret);  // ['value' => '[REDACTED]', 'length' => 11]

Sicherheit

Encryption

  • Algorithmus: Libsodium sodium_crypto_secretbox (XSalsa20 + Poly1305)
  • Authenticated Encryption: Schutz gegen Tampering
  • Random Nonce: Eindeutig pro Secret
  • 256-bit Keys: SODIUM_CRYPTO_SECRETBOX_KEYBYTES (32 bytes)

Audit Logging

Alle Vault-Operationen werden geloggt:

SELECT * FROM vault_audit_log
WHERE secret_key = 'database.password'
ORDER BY timestamp DESC;

Geloggte Informationen:

  • Secret Key (nicht der Value!)
  • Action (read, write, delete, rotate, export)
  • Timestamp
  • User ID (wenn verfügbar)
  • IP Address
  • User Agent
  • Success/Failure Status
  • Error Message (bei Fehlern)

Best Practices

DO:

  • Encryption Key in .env speichern (gitignored)
  • Unterschiedliche Keys für Environments
  • Regelmäßige Key Rotation (quartalsweise)
  • Audit Logs monitoren
  • Secrets mit beschreibenden Namen (api.service.key)

DON'T:

  • Encryption Key committen
  • Secrets in Code hardcoden
  • Gleichen Key für Dev/Staging/Production
  • Audit Logs ignorieren
  • Secrets ohne Namespace (password)

Migration von .env zu Vault

Schritt 1: Secrets identifizieren

Identifiziere alle sensiblen Werte in .env:

  • Passwörter
  • API Keys
  • Client Secrets
  • Private Keys
  • Tokens

Schritt 2: In Vault migrieren

# Für jeden Secret:
docker exec php php console.php vault:set api.stripe.secret_key "sk_live_..."
docker exec php php console.php vault:set database.password "prod-password"
docker exec php php console.php vault:set oauth.spotify.client_secret "abc123..."

Schritt 3: Code aktualisieren

Vorher (.env):

$apiKey = $_ENV['STRIPE_SECRET_KEY'];  // ❌ Unsicher

Nachher (Vault):

$apiKey = $this->vault
    ->get(SecretKey::from('api.stripe.secret_key'))
    ->reveal();  // ✅ Sicher

Schritt 4: .env bereinigen

Nach Migration Secrets aus .env entfernen:

- STRIPE_SECRET_KEY=sk_live_...
- DATABASE_PASSWORD=prod-password
+ # Secrets now in Vault - use vault:get to retrieve

Troubleshooting

"Vault not available"

Problem: VAULT_ENCRYPTION_KEY nicht gesetzt

Lösung:

# Key generieren
docker exec php php console.php vault:generate-key

# Key in .env hinzufügen
echo "VAULT_ENCRYPTION_KEY=generated_key_here" >> .env

"Sodium extension required"

Problem: PHP Sodium Extension fehlt

Lösung: Sodium ist in PHP 7.2+ standardmäßig verfügbar. Prüfe PHP Version:

docker exec php php -m | grep sodium

"Decryption failed"

Problem: Falscher Encryption Key oder korrupte Daten

Mögliche Ursachen:

  • Encryption Key geändert ohne Re-Encryption
  • Database Corruption
  • Falsche Encoding (base64)

Lösung:

  • Richtigen Key aus Backup wiederherstellen
  • Oder Key Rotation durchführen

"Secret not found"

Problem: Secret Key existiert nicht

Lösung:

# Alle Keys auflisten
docker exec php php console.php vault:list

# Secret neu erstellen
docker exec php php console.php vault:set your-key "your-value"

Performance

Benchmarks

  • Read Operation: ~5ms (inkl. Decryption)
  • Write Operation: ~10ms (inkl. Encryption + DB Insert)
  • Key Rotation: ~100ms pro Secret

Optimierungen

Für High-Performance Scenarios:

  • Secrets cachen (mit TTL)
  • Batch-Operations für Multiple Secrets
  • Read Replicas für Read-Heavy Workloads

Testing

# Unit Tests ausführen
docker exec php ./vendor/bin/pest tests/Framework/Vault/

Tests validieren:

  • Value Object Validierung
  • Encryption/Decryption
  • Key Generation
  • Audit Logging

Weitere Informationen