- Add `FileOwnership` to encapsulate file owner and group information. - Add `ProcessUser` to represent and manage system process user details. - Enhance ownership matching and debugging with structured data objects. - Include new documentation on file ownership handling and permission improvements. - Prepare infrastructure for enriched error handling in filesystem operations.
10 KiB
Redis Authentication Problem Analysis & Refactoring Plan
Problem Description
Fehlermeldung:
Direct Redis connection failed: Failed to connect to Redis (cache): WRONGPASS invalid username-password pair or user is disabled.
with Host: staging-redis and Password: gRUFGtFFthCGFHYjZ7o9yuiDRJ0JdG4Syiv1fVPb544=
Root Cause Analysis
Identified Issues
-
Password Whitespace/Newline Problem
- Das Passwort wird aus Docker Secrets geladen (
/run/secrets/redis_password) DockerSecretsResolververwendettrim(), aber m?glicherweise gibt es unsichtbare Zeichen (z.B.\r\n,\n, Tabs)- Redis ist sehr strikt bei Passwort-?bereinstimmungen
- Das Passwort wird aus Docker Secrets geladen (
-
Fehlende Passwort-Validierung
- Keine Validierung des Passworts vor der Verwendung
- Keine Sanitization au?erhalb von
trim()
-
Unzureichende Fehlerbehandlung
- Fehlermeldung enth?lt das vollst?ndige Passwort (Security Risk!)
- Keine Unterscheidung zwischen verschiedenen Auth-Fehlertypen
- Keine detaillierte Diagnose-Informationen
-
Keine Redis ACL Unterst?tzung
- Aktuell nur Legacy Passwort-Auth (
requirepass) - Redis 6+ unterst?tzt ACL mit Username/Password
- Code ist nicht f?r zuk?nftige ACL-Nutzung vorbereitet
- Aktuell nur Legacy Passwort-Auth (
-
Fehlende Passwort-Normalisierung
- Verschiedene Whitespace-Zeichen werden nicht normalisiert
- Keine Behandlung von UTF-8 BOM oder anderen Steuerzeichen
Current Implementation Analysis
Code Flow
Environment::get(REDIS_PASSWORD)
?
DockerSecretsResolver::resolve()
?
file_get_contents() + trim()
?
RedisConfig::fromEnvironment()
?
RedisConnection::connect()
?
$client->auth($password) ? WRONGPASS Fehler hier
Problematic Code Sections
1. DockerSecretsResolver.php (Zeile 104)
$result = trim($content);
- Problem: Entfernt nur Whitespace am Anfang/Ende
- Fehlt: Normalisierung von
\r\nzu\n, Entfernung von unsichtbaren Zeichen
2. RedisConnection.php (Zeile 92-95)
if ($this->config->password) {
if (! $this->client->auth($this->config->password)) {
throw new RedisConnectionException("Redis authentication failed");
}
}
- Problem: Generische Fehlermeldung ohne Details
- Fehlt: Unterscheidung zwischen verschiedenen Auth-Fehlern
- Fehlt: Passwort-Validierung vor Auth-Versuch
3. RedisConnection.php (Zeile 115)
throw new RedisConnectionException(
"Failed to connect to Redis ({$this->name}): " . $e->getMessage() . " with Host: {$this->config->host} and Password: {$this->config->password}",
previous: $e
);
- KRITISCH: Passwort wird in Exception-Message ausgegeben (Security Risk!)
- Passwort sollte niemals in Logs/Exceptions erscheinen
Refactoring Vorschl?ge
1. Passwort-Normalisierung verbessern
Problem: trim() ist unzureichend f?r Docker Secrets
L?sung: Erweiterte Normalisierung in DockerSecretsResolver
private function normalizeSecret(string $content): string
{
// Entferne alle Whitespace-Zeichen am Anfang und Ende
$normalized = trim($content);
// Entferne unsichtbare Steuerzeichen (au?er normale Zeichen)
$normalized = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $normalized);
// Normalisiere Line Endings (falls vorhanden)
$normalized = str_replace(["\r\n", "\r"], "", $normalized);
// Entferne UTF-8 BOM falls vorhanden
if (str_starts_with($normalized, "\xEF\xBB\xBF")) {
$normalized = substr($normalized, 3);
}
return $normalized;
}
2. Passwort-Validierung hinzuf?gen
Problem: Keine Validierung vor Verwendung
L?sung: Validierung in RedisConfig
private function validatePassword(?string $password): ?string
{
if ($password === null || $password === '') {
return null;
}
// Warnung f?r sehr kurze Passw?rter
if (strlen($password) < 8) {
// Log warning, aber nicht blockieren (kann gewollt sein)
}
// Warnung f?r Whitespace in der Mitte (nur trimmen, nicht blockieren)
$trimmed = trim($password);
if ($trimmed !== $password) {
// Log warning: Password hatte Whitespace am Anfang/Ende
}
return $trimmed !== '' ? $trimmed : null;
}
3. Sicherheit: Passwort aus Exceptions entfernen
Problem: Passwort erscheint in Exception-Messages
L?sung: Maskiertes Passwort in Fehlermeldungen
private function maskPassword(?string $password): string
{
if ($password === null || $password === '') {
return '(not set)';
}
$length = strlen($password);
if ($length <= 4) {
return str_repeat('*', $length);
}
return substr($password, 0, 2) . str_repeat('*', $length - 4) . substr($password, -2);
}
4. Verbesserte Fehlerbehandlung
Problem: Generische Fehlermeldungen ohne Kontext
L?sung: Detaillierte Exception mit diagnostischen Informationen
private function authenticate(): void
{
if (!$this->config->password) {
return; // Keine Authentifizierung erforderlich
}
try {
$authResult = $this->client->auth($this->config->password);
if (!$authResult) {
throw new RedisConnectionException(
"Redis authentication failed for connection '{$this->name}'",
context: $this->createAuthDiagnostics()
);
}
} catch (RedisException $e) {
$message = $e->getMessage();
// Spezifische Fehlermeldungen basierend auf Redis-Fehlercode
if (str_contains($message, 'WRONGPASS')) {
throw new RedisConnectionException(
"Redis authentication failed: Invalid password for connection '{$this->name}'",
context: $this->createAuthDiagnostics(),
previous: $e
);
}
// Andere Auth-Fehler
throw new RedisConnectionException(
"Redis authentication error for connection '{$this->name}': {$message}",
context: $this->createAuthDiagnostics(),
previous: $e
);
}
}
private function createAuthDiagnostics(): ExceptionContext
{
$context = ExceptionContext::forOperation('Redis Authentication');
$context->add('connection_name', $this->name);
$context->add('host', $this->config->host);
$context->add('port', $this->config->port);
$context->add('database', $this->config->database);
$context->add('password_set', $this->config->password !== null);
$context->add('password_length', $this->config->password ? strlen($this->config->password) : 0);
// KEIN Passwort in Context!
return $context;
}
5. Redis ACL Unterst?tzung (Optional, f?r Zukunft)
Problem: Keine Unterst?tzung f?r Redis 6+ ACL
L?sung: Optionale Username/Password Auth
final readonly class RedisConfig
{
private function __construct(
public string $host = 'redis',
public int $port = 6379,
public ?string $password = null,
public ?string $username = null, // NEU f?r ACL
// ... rest
) {}
public static function fromEnvironment(Environment $env): self
{
return new self(
// ...
password: $env->get(EnvKey::REDIS_PASSWORD, null),
username: $env->get(EnvKey::REDIS_USERNAME, null), // NEU
// ...
);
}
}
// In RedisConnection.php
private function authenticate(): void
{
if (!$this->config->password) {
return;
}
// Redis 6+ ACL: auth(username, password)
if ($this->config->username) {
$authResult = $this->client->auth($this->config->username, $this->config->password);
} else {
// Legacy: auth(password)
$authResult = $this->client->auth($this->config->password);
}
if (!$authResult) {
throw new RedisConnectionException(/* ... */);
}
}
6. EnvKey Enum erweitern
L?sung: Optional REDIS_USERNAME f?r zuk?nftige ACL-Nutzung
enum EnvKey: string
{
// ... existing ...
case REDIS_HOST = 'REDIS_HOST';
case REDIS_PORT = 'REDIS_PORT';
case REDIS_PASSWORD = 'REDIS_PASSWORD';
case REDIS_USERNAME = 'REDIS_USERNAME'; // NEU (optional)
case REDIS_PREFIX = 'REDIS_PREFIX';
}
Implementierungsplan
Phase 1: Kritische Sicherheitsfixes (SOFORT)
-
? Passwort aus Exception-Messages entfernen
RedisConnection.phpZeile 115: Maskiertes Passwort verwenden- Security Hotfix
-
? Verbesserte Passwort-Normalisierung
DockerSecretsResolver.php: ErweitertenormalizeSecret()Methode- Behebt wahrscheinlich das Hauptproblem
Phase 2: Robustheit (HOCH)
-
? Passwort-Validierung
RedisConfig: Validierung und Sanitization- Warnungen f?r problematische Passw?rter
-
? Verbesserte Fehlerbehandlung
- Spezifische Exception-Messages
- Diagnostische Informationen (ohne Passwort!)
Phase 3: Zukunftssicherheit (MITTEL)
- ?? Redis ACL Unterst?tzung (Optional)
- Nur wenn Redis ACL verwendet werden soll
- Backward-kompatibel mit Legacy Auth
Priorisierung
| Priorit?t | Task | Impact | Effort |
|---|---|---|---|
| ?? KRITISCH | Passwort aus Exceptions entfernen | Security | Low |
| ?? KRITISCH | Passwort-Normalisierung verbessern | Fixes Bug | Low |
| ?? HOCH | Passwort-Validierung | Robustheit | Low |
| ?? HOCH | Verbesserte Fehlerbehandlung | Debugging | Medium |
| ?? MITTEL | Redis ACL Unterst?tzung | Zukunft | Medium |
Testing Recommendations
-
Unit Tests f?r Passwort-Normalisierung
test('normalizes password with newlines', function () { $resolver = new DockerSecretsResolver(); // Test with \r\n, \n, \r, etc. }); -
Integration Tests f?r Redis Auth
test('connects with password from Docker Secret', function () { // Test with actual Redis instance }); -
Security Tests
test('does not expose password in exception messages', function () { // Verify password is masked });
Expected Outcomes
Nach Implementierung:
- ? Passwort wird korrekt normalisiert (Whitespace entfernt)
- ? Keine Passw?rter in Logs/Exceptions
- ? Bessere Fehlermeldungen f?r Debugging
- ? Robustere Passwort-Behandlung
- ? Vorbereitung f?r Redis ACL (optional)
Notes
- Das Hauptproblem ist wahrscheinlich Whitespace/Newlines im Passwort
- Die Security-Probleme (Passwort in Exceptions) sollten sofort behoben werden
- ACL-Unterst?tzung ist optional, aber gut f?r Zukunftssicherheit