# barox Security Vulnerability Reporting Tool

Öffentliches Meldeportal für Sicherheitsprobleme/Schwachstellen in barox
Produkten gemäss **EU Cyber Resilience Act (CRA)** mit internem Admin-Bereich
für Coordinated Vulnerability Disclosure, CRA-Fristenverfolgung (24 h / 72 h /
Abschlussbericht) und Audit-Log.

- Öffentlicher Bereich **dreisprachig (DE/FR/EN)**, Sprachumschalter im Header
- Meldeformular mit optional anonymer Meldung und sicherem Datei-Upload
- Admin: Dashboard mit Filtern/Suche, Fallansicht, Status-Workflow,
  interne Kommentare, CRA-Felder, CSV-/PDF-Export, Audit-Log
- Reines PHP 8.x ohne Framework/Composer-Abhängigkeiten

## Projektstruktur

```
barox-cve/
├── public/            # Webroot (einziger öffentlich erreichbarer Ordner)
│   ├── index.php      # Front-Controller / Router
│   ├── .htaccess      # Apache-Rewrites + Härtung
│   └── assets/        # CSS, JS, Logos, Fonts
├── src/               # PHP-Klassen (Barox\…), bootstrap.php, helpers.php
│   ├── Core/          # Auth, Csrf, Session, RateLimiter, Uploader, Mailer, Pdf …
│   ├── Controllers/   # Public, Admin, Attachment, Export
│   └── Lang/          # de.php / en.php (öffentlicher Bereich)
├── templates/         # PHP-Templates (public/, admin/, email/, errors/, layout/)
├── storage/           # NICHT im Webroot: uploads/, logs/, tmp/
├── config/            # .env (Secrets) + .env.example
├── database/          # schema.sql, add_admin.php, migrations/
├── bin/               # cleanup.php (Cronjob)
└── assets/            # Design-Quellen (CD-Manual, Original-Logos/-Fonts)
```

## Voraussetzungen

- Ubuntu 22.04/24.04 LTS
- PHP ≥ 8.1 mit `php-mysql` (PDO), `php-mbstring`, `php-fileinfo`, `php-curl`,
  `php-openssl` (für die Microsoft-365-Anmeldung)
- MySQL 8 oder MariaDB ≥ 10.6
- Apache 2.4 **oder** Nginx + PHP-FPM
- TLS-Zertifikat (z. B. Let's Encrypt) – **HTTPS ist Pflicht**
- optional: `clamav-daemon` für den Virenscan-Hook

## Installation auf Ubuntu

```bash
sudo apt update
sudo apt install -y php php-fpm php-mysql php-mbstring mariadb-server
# optional für Virenscan:
sudo apt install -y clamav-daemon

# Projekt ablegen (Beispielpfad)
sudo mkdir -p /var/www/barox-security
sudo rsync -a --exclude /assets ./ /var/www/barox-security/   # /assets (Design-Quellen) muss nicht aufs Produktivsystem
sudo chown -R root:www-data /var/www/barox-security
```

### Datenbank-Setup

Der Datenbankname muss exakt dem Wert `DB_NAME` in `config/.env` entsprechen
(Beispiel hier: `barox_cve`):

```bash
sudo mysql -e "CREATE DATABASE barox_cve CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
sudo mysql barox_cve < database/schema.sql
sudo mysql -e "CREATE USER 'barox'@'localhost' IDENTIFIED BY 'STARKES_PASSWORT';
GRANT SELECT,INSERT,UPDATE,DELETE ON barox_cve.* TO 'barox'@'localhost';
FLUSH PRIVILEGES;"

# Prüfen: müssen 8 Tabellen sein (users, vulnerability_reports, …)
sudo mysql -e "SHOW TABLES" barox_cve
```

Der App-Benutzer erhält bewusst **kein** `DROP/ALTER/CREATE` (Least Privilege).

### Konfiguration (.env)

```bash
cd /var/www/barox-security
cp config/.env.example config/.env
php -r "echo bin2hex(random_bytes(32)), PHP_EOL;"   # Wert für APP_SECRET
nano config/.env                                     # DB, SMTP, APP_URL eintragen

sudo chown root:www-data config/.env
sudo chmod 640 config/.env
```

Alle Secrets liegen ausschliesslich in `config/.env` (nicht im Code, nicht im
Webroot, per `.gitignore` von Versionierung ausgeschlossen).

### Rechte für Upload-/Storage-Ordner

```bash
sudo chown -R www-data:www-data storage
sudo chmod 750 storage storage/uploads storage/logs storage/tmp
```

Uploads werden **ausserhalb des Webroots** (`storage/uploads/`) mit
randomisiertem Namen und Endung `.dat` ohne Execute-Rechte gespeichert und nur
über den authentifizierten Download-Controller ausgeliefert.

### Admin-Anmeldung: Microsoft 365 / Entra ID

Der Admin-Bereich wird per **Microsoft-Konto** geschützt (OpenID Connect,
Authorization Code Flow). Ein lokaler Passwort-Login existiert nicht mehr.
Zugang erhalten ausschliesslich E-Mail-Adressen, die **vorab** hinterlegt wurden.

**1. App-Registrierung** im Entra Admin Center (entra.microsoft.com):

- „App-Registrierungen“ → „Neue Registrierung“, **Single-Tenant**.
- Redirect-URI (Typ *Web*): `https://security.barox.ch/admin/auth/callback`
- „Zertifikate & Geheimnisse“ → Client-Secret erzeugen.
- Werte in `config/.env` eintragen: `MS_TENANT_ID`, `MS_CLIENT_ID`,
  `MS_CLIENT_SECRET` (optional `MS_REDIRECT_URI`).

**2. Bei bestehender Datenbank** einmalig die Migration einspielen:

```bash
mysql <db-name> < database/migrations/2026-07-ms365-auth.sql
```

**3. Zugangsberechtigte freischalten** (Allowlist in der `users`-Tabelle):

```bash
php database/add_admin.php max.muster@barox.ch "Max Muster" admin
# Rolle 'analyst' für weitere Bearbeiter; Zugang entziehen:
php database/add_admin.php max.muster@barox.ch --disable
```

Nur wer hier eingetragen ist, kann sich anschliessend unter `/admin/login`
per Microsoft anmelden. Beim ersten Login wird das Konto an die Entra-Objekt-ID
(`oid`) gebunden.

## Apache-Konfiguration

```apache
<VirtualHost *:443>
    ServerName security.barox.ch
    DocumentRoot /var/www/barox-security/public

    SSLEngine on
    SSLCertificateFile      /etc/letsencrypt/live/security.barox.ch/fullchain.pem
    SSLCertificateKeyFile   /etc/letsencrypt/live/security.barox.ch/privkey.pem

    <Directory /var/www/barox-security/public>
        AllowOverride All        # aktiviert public/.htaccess
        Require all granted
    </Directory>

    # Upload-Limits an .env (UPLOAD_MAX_MB) angleichen
    php_admin_value upload_max_filesize 12M
    php_admin_value post_max_size 64M

    ErrorLog  ${APACHE_LOG_DIR}/barox-security-error.log
    CustomLog ${APACHE_LOG_DIR}/barox-security-access.log combined
</VirtualHost>

<VirtualHost *:80>
    ServerName security.barox.ch
    Redirect permanent / https://security.barox.ch/
</VirtualHost>
```

```bash
sudo a2enmod rewrite ssl
sudo a2ensite barox-security
sudo systemctl reload apache2
```

## Nginx-Konfiguration

```nginx
server {
    listen 443 ssl http2;
    server_name security.barox.ch;
    root /var/www/barox-security/public;
    index index.php;

    ssl_certificate     /etc/letsencrypt/live/security.barox.ch/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/security.barox.ch/privkey.pem;

    client_max_body_size 64m;   # an Upload-Limits angleichen

    location / {
        try_files $uri /index.php$is_args$args;
    }

    # PHP nur für den Front-Controller
    location = /index.php {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
    location ~ \.php$ { return 404; }

    location ~ /\. { deny all; }
}

server {
    listen 80;
    server_name security.barox.ch;
    return 301 https://$host$request_uri;
}
```

## Cronjobs

```cron
# /etc/cron.d/barox-security  (als www-data)
17 * * * * www-data /usr/bin/php /var/www/barox-security/bin/cleanup.php >> /var/www/barox-security/storage/logs/cleanup.log 2>&1
```

`bin/cleanup.php` bereinigt Rate-Limit-Fenster, hebt abgelaufene Kontosperren
auf, meldet verwaiste Upload-Dateien und scannt Anhänge mit Status `pending`
nach (ClamAV-Hook).

## E-Mail

SMTP wird komplett über `.env` konfiguriert (`SMTP_HOST` leer = Versand aus).
Versendet werden:

- Eingangsbestätigung an den Melder (falls E-Mail angegeben; Sprache DE/FR/EN
  gemäss Formularsprache) – ohne Meldungsdetails
- interne Benachrichtigung an `security@barox.ch`; kritische Meldungen oder
  aktive Ausnutzung mit Betreff-Präfix **[KRITISCH]** und rotem Banner
- **keine Anhänge per Mail** – nur Fallnummer/Link in den Admin-Bereich

## Sicherheitsfunktionen (implementiert)

| Bereich | Umsetzung |
|---|---|
| SQL-Injection | PDO Prepared Statements (Emulation deaktiviert), Least-Privilege-DB-User |
| XSS | konsequentes Output-Escaping via `e()` in allen Templates, strikte CSP |
| CSRF | Session-Token + `hash_equals()` für alle POST-Routen, SameSite=Lax; OAuth-`state` gegen Login-CSRF |
| Session | HttpOnly/Secure/SameSite=Lax-Cookies, strict mode, Idle-/Absolut-Timeout, ID-Rotation bei Login, UA-Bindung |
| Login | Microsoft 365 / Entra ID (OIDC Auth Code Flow), E-Mail-Allowlist, id_token-Signatur- (RS256/JWKS) und Claim-Prüfung (iss/aud/nonce/exp), `state`-CSRF-Schutz, Bindung an Entra-`oid` |
| Rate Limiting | öffentliches Formular (konfigurierbar via `settings.report_rate_limit`) und Admin-Anmeldung |
| Spam | Honeypot-Feld + Zeitfalle; CAPTCHA-Hook vorbereitet (`CAPTCHA_ENABLED`) |
| Uploads | Allowlist (txt, log, pdf, png, jpg, zip), finfo-MIME-Prüfung, Grössenlimit, Speicherung ausserhalb Webroot, randomisierte Namen ohne ausführbare Endung, ClamAV-Hook, Download nur authentifiziert als `application/octet-stream` |
| Header | CSP, X-Frame-Options DENY, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, HSTS |
| Fehler | keine Details im Frontend (`APP_ENV=production`), Logging nach `storage/logs/` |
| DSGVO | IP nur als HMAC-Hash, anonyme Meldung, Datenschutzseite DE/FR/EN, Datenminimierung in Mails |
| Audit | `audit_log` für alle relevanten Aktionen, `report_status_history` mit Zeitstempeln, Exporte enthalten Audit-Informationen |

## Backup

- **Datenbank:** täglicher Dump, z. B.
  `mysqldump --single-transaction barox_vulndb | gzip > /backup/barox_vulndb_$(date +%F).sql.gz`
- **Dateien:** `storage/uploads/` und `config/.env` in das Backup einbeziehen
- Backups verschlüsselt und zugriffsbeschränkt lagern (enthalten
  Schwachstellendetails!); Restore regelmässig testen
- Aufbewahrung an CRA-Dokumentationspflichten ausrichten

## Security-Hardening (Server)

- `unattended-upgrades` aktivieren; PHP/DB aktuell halten
- PHP: `expose_php=Off`, `display_errors=Off`, `open_basedir` auf Projektpfad
- MariaDB nur auf `127.0.0.1` binden
- Firewall: nur 80/443 (+ SSH mit Key-Auth) offen; fail2ban für SSH
- `/admin` optional zusätzlich per IP-Allowlist oder VPN einschränken
- TLS: nur TLS 1.2/1.3, HSTS ist app-seitig gesetzt
- ClamAV aktivieren (`CLAMAV_ENABLED=true`) sobald `clamav-daemon` läuft
- Monitoring der Logs `storage/logs/` und des Mailversands

## Erweiterungspunkte (im Code markiert)

- CAPTCHA-Integration: `PublicController::submit()` + `templates/public/form.php`
- PGP-Key / `security.txt`: `templates/public/home.php`
- PDF-Layout: `src/Core/Pdf.php` kann 1:1 durch dompdf/FPDF ersetzt werden
- Mailer: `src/Core/Mailer.php` austauschbar gegen PHPMailer/Symfony Mailer
