Ergebnis einer beauftragten Sicherheitsbegutachtung. Die WebAuthn- Zeremonie selbst ist nicht angefasst worden - sie war in Ordnung. Alle Befunde lagen DANEBEN: Ein zweiter Faktor ist nur so stark wie der schwaechste Weg zum Konto, und Sitzungen, die aus ihm entstehen, erben seinen Schutz nicht. KRITISCH /api/setup/v1/login prueste auf totp_secret - eine Spalte, die kein Anmeldeweg auswertet - und nicht auf dc_user_credentials. Ein Administratorpasswort genuegte dort fuer ein setup:install-Token ohne Hardwarekey. Neu: Konten mit zweitem Faktor abgewiesen, und die Rollentrennung gilt symmetrisch. Der Docblock behauptete sie seit jeher; erzwungen hat sie nichts. HOCH secondFactorRequired(), accountLockoutSeconds() und lockoutSeconds() fingen jedes Throwable und fielen auf "kein Faktor" bzw. "nicht gesperrt" zurueck. Gefangen wurden damit auch Verbindungsabbrueche und Lock-Timeouts - jeder davon schaltete still fuer alle Konten auf Passwort-allein. Toleriert werden jetzt nur noch 42S02 und 42S22. HOCH Fuenf Aktionen aenderten die Anmeldemittel ohne erneute Bestaetigung. Eine uebernommene Sitzung konnte sich damit dauerhaft einnisten. Neu: Authentifizierung aus den letzten zehn Minuten noetig, geprueft an derselben Stelle, an der auch der Leerlauf sperrt. WEITER - Leerlauf 30 statt 120 Minuten, und GESPERRT statt beendet: AAL2 laesst zur Reaktivierung einen Faktor genuegen. Wer alle 30 Minuten den Hardwarekey vorzeigen muss, laesst ihn stecken. - Migration 022: session_epoch. Ein Passwortwechsel beendet die uebrigen Sitzungen des Kontos, die eigene bleibt. - Kontosperre wirkt auf der zweiten Stufe; Zaehler gegen unbegrenztes Raten im Zwischenzustand. - Drosselung auf login.php (10/60s) und /api/auth/v1/ (30/60s). - Anmeldefehler ununterscheidbar; die IP-Sperre darf benannt bleiben, sie sagt nichts ueber ein Konto. - HSTS, TLS-Zwang VOR allen Routen, CSP. Die CSP traegt wegen 93 Inline-Handlern nicht gegen XSS - das steht so im Kommentar. - session.use_strict_mode, CSRF-Token bei der Anmeldung verworfen, Secure-Flag aus app.url statt aus $_SERVER['HTTPS']. - rpId/expectedOrigin nicht mehr aus dem Host-Header. /api/health meldet unter checks.webauthn_anchor, ob der Anker steht. - Notfalltoken laeuft nach 180 Tagen ab, geprueft nach dem Hashvergleich. - config.example.php ohne echtes Passwort, verify_ssl auf true. - Serverdaten.txt.bak mitgesperrt, dc_login_attempts raeumt auf. BELEGT 188 von 188 Testfaellen gruen, darunter drei neue in der Gruppe "haertung". Die Blockade einer veralteten Sitzung liess sich nicht in der Suite pruefen - sie verlangt, das Zehn-Minuten-Fenster wirklich abzuwarten. Dafuer gibt es ein Werkzeug ausserhalb; es belegt, dass der Formularweg auf den Sperrbildschirm fuehrt, die Regel dabei unveraendert bleibt, der JSON-Weg reauth_required meldet und das Passwort allein wieder oeffnet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
100 lines
4.6 KiB
PHP
100 lines
4.6 KiB
PHP
<?php
|
||
/**
|
||
* Deploymentcenter Configuration – VORLAGE
|
||
*
|
||
* Diese Datei nach config/config.php kopieren und ausfüllen.
|
||
* config/config.php ist per .gitignore vom Repository ausgeschlossen.
|
||
*
|
||
* Jeder Wert kann alternativ über eine Umgebungsvariable gesetzt werden
|
||
* (siehe dc_env() unten). Die Umgebungsvariable hat immer Vorrang.
|
||
*/
|
||
|
||
if (!function_exists('dc_env')) {
|
||
/**
|
||
* Liest eine Umgebungsvariable, fällt sonst auf den Standardwert zurück.
|
||
*/
|
||
function dc_env(string $key, $default = null)
|
||
{
|
||
$val = getenv($key);
|
||
if ($val === false || $val === '') {
|
||
return $default;
|
||
}
|
||
return $val;
|
||
}
|
||
}
|
||
|
||
return [
|
||
'app' => [
|
||
'name' => 'Deploymentcenter',
|
||
// Rueckfallebene. Massgeblich ist "current" aus
|
||
// public/docs/changelog.json - src/bootstrap.php setzt den Wert von
|
||
// dort. Hier wurde der Wert nicht nachgezogen - /api/health meldete
|
||
// deshalb bis 2.7.0 unveraendert 2.0.0.
|
||
'version' => '2.0.0',
|
||
'url' => dc_env('DC_APP_URL', 'https://dc.example.com'),
|
||
'timezone' => 'Europe/Berlin',
|
||
// Bei true werden Exception-Texte in API-Antworten ausgegeben.
|
||
// Auf Produktivsystemen zwingend false lassen.
|
||
'debug' => (bool)dc_env('DC_DEBUG', false),
|
||
],
|
||
'db' => [
|
||
'host' => dc_env('DC_DB_HOST', 'localhost'),
|
||
'dbname' => dc_env('DC_DB_NAME', 'deploymentcenter'),
|
||
'username' => dc_env('DC_DB_USER', 'root'),
|
||
'password' => dc_env('DC_DB_PASS', ''),
|
||
'charset' => 'utf8mb4',
|
||
],
|
||
'security' => [
|
||
// Master-Key für Server-zu-Server-Aufrufe (Evaluator-Cron, Deactivate, Migration).
|
||
// Mit `openssl rand -hex 32` erzeugen.
|
||
'shared_key' => dc_env('DC_SHARED_KEY', ''),
|
||
'session_name' => 'DC_SESSION_ID',
|
||
// Signaturschlüssel für ausgehende Webhooks (HMAC-SHA256).
|
||
'webhook_key' => dc_env('DC_WEBHOOK_KEY', ''),
|
||
// Signaturschlüssel für Offline-Lizenzdateien (.lic).
|
||
'license_key' => dc_env('DC_LICENSE_SIGNING_KEY', ''),
|
||
// Privater RSA-Schlüssel, mit dem Releases signiert werden. Entweder
|
||
// das PEM selbst oder ein Pfad zur Schlüsseldatei.
|
||
// openssl genrsa -out /etc/dc/release-signing.pem 2048
|
||
// chmod 600 /etc/dc/release-signing.pem
|
||
// Leer lassen schaltet das Signieren ab; Releases bleiben dann
|
||
// installierbar, der Agent weist aber bei jedem Update darauf hin.
|
||
// Bewusst asymmetrisch: geprüft wird auf fremden Systemen, dort darf
|
||
// der Signierschlüssel nicht liegen.
|
||
'release_private_key' => dc_env('DC_RELEASE_SIGNING_KEY', ''),
|
||
// Proaktive Kontosperren & Brute-Force-Schutz
|
||
'max_account_attempts' => (int)dc_env('DC_MAX_ACCOUNT_ATTEMPTS', 5),
|
||
'account_lockout_minutes' => (int)dc_env('DC_ACCOUNT_LOCKOUT_MINUTES', 15),
|
||
'max_ip_attempts' => (int)dc_env('DC_MAX_IP_ATTEMPTS', 10),
|
||
'use_syslog' => (bool)dc_env('DC_USE_SYSLOG', false),
|
||
],
|
||
'bugtracker' => [
|
||
// Projekt-Slug, unter dem das Deploymentcenter eigene Fehler meldet.
|
||
'self_project' => 'deploymentcenter',
|
||
// Reports pro IP und Minute an /api/bugtracker/v1/report.
|
||
'report_rate' => 60,
|
||
// Reports pro IP und Minute an /api/errors/v1/report. Bewusst höher:
|
||
// Dort meldet ein globaler Exception-Handler, und ein Fehlerschauer
|
||
// erzeugt in Sekunden mehr Meldungen als ein Mensch am Tag.
|
||
'error_rate' => 300,
|
||
// Wie lange ein Agent ein Item exklusiv beansprucht (Minuten).
|
||
'lease_minutes' => 30,
|
||
],
|
||
'rocketchat' => [
|
||
'enabled' => (bool)dc_env('DC_RC_ENABLED', true),
|
||
'url' => dc_env('DC_RC_URL', 'https://chat.wh1.mhdf.de'),
|
||
'username' => dc_env('DC_RC_USER', 'deploymentcenter'),
|
||
// Leer lassen und ueber die Umgebungsvariable setzen. In dieser
|
||
// Datei stand bis 2.9.3 ein echtes Passwort - eine Beispieldatei
|
||
// liegt im Repository, und damit lag es dort auch.
|
||
'password' => dc_env('DC_RC_PASS', ''),
|
||
'status_channel' => dc_env('DC_RC_STATUS_CHANNEL', '#DC-Systemstatus'),
|
||
'alert_channel' => dc_env('DC_RC_ALERT_CHANNEL', '#DC-Alerts'),
|
||
// Vorgabe true. Eine abgeschaltete Zertifikatspruefung ist in einer
|
||
// Beispieldatei die denkbar schlechteste Vorgabe: Sie wird
|
||
// uebernommen, ohne dass jemand die Entscheidung trifft.
|
||
'verify_ssl' => (bool)dc_env('DC_RC_VERIFY_SSL', true),
|
||
'report_interval' => (int)dc_env('DC_RC_REPORT_INTERVAL', 43200),
|
||
],
|
||
];
|