ci(quick-261009-p0m): ZAP-Grundpruefung gegen alpha vor Freigaben, erster Lauf

- zap-baseline.sh: passive Aussenpruefung mit festgelegtem ZAP-Abbild, Ziel per ZAP_TARGET (Standard: oeffentliche Adresse von alpha), Anmeldedatei-Weg nur fuer den Ausnahmefall
- zap-hooks.py: Spinne fuellt keine Formulare aus und sendet keine (POST=0 lokal bewiesen)
- Sicherheitsprotokoll: Abschnitt Pruefung von aussen, erster Lauf direkt gegen die Anwendung (0 hoch, 2 mittel, 6 niedrig, 3 Info), Einordnung und Verlauf
- Betriebsanleitung Kapitel 9: Schritt Pruefung von aussen; Entwicklungsanleitung: Abschnitt ZAP; CHANGELOG

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-09 19:43:46 +02:00
parent 5b36b9a564
commit 13571df994
6 changed files with 276 additions and 2 deletions
+158
View File
@@ -0,0 +1,158 @@
#!/bin/sh
# zap-baseline.sh -- Grundpruefung von aussen vor einer Freigabe (quick-261009-p0m).
#
# Prueft den Testserver (alpha) wie ein Besucher ohne Konto, mit dem OWASP-ZAP-Abbild:
# Startseite, Anmeldeseite, Kopfzeilen, Cookies und ausgelieferte Dateien werden
# abgerufen und passiv ausgewertet. Es ist KEIN Angriffswerkzeug im Einsatz:
# - nur die klassische Spinne (Links folgen), kein AJAX-Spider (der wuerde das
# Anmeldeformular ausfuellen und absenden),
# - Formulare werden weder ausgefuellt noch abgesendet (Haken zap-hooks.py),
# - nur passive Regeln, kein aktiver Scan, also keine Anfragen mit Schadmustern.
# Laeuft von Hand auf dem Entwicklungsrechner, nicht in der Pipeline.
#
# Aufrufe (aus dem Repository-Wurzelverzeichnis):
# sh .gitea/scripts/zap-baseline.sh # Lauf gegen das Standardziel
# ZAP_TARGET=http://HOST:3000 sh .gitea/scripts/zap-baseline.sh
# sh .gitea/scripts/zap-baseline.sh --print-plan # nur anzeigen, ohne Netz
#
# Umgebungsvariablen:
# ZAP_TARGET Ziel-Adresse (Standard: https://alpha.tessera.ctl.de). Wer den
# vorgeschalteten Proxy umgehen will, gibt die direkte Adresse
# der Anwendung an (http://HOST:3000).
# ZAP_SPIDER_MINUTES Hoechstdauer der Spinne in Minuten (Standard: 3)
# ZAP_REPORT_DIR Berichtsordner (Standard: security-reports/zap-{UTC-Zeit});
# security-reports/ steht in .gitignore und .dockerignore
# ZAP_DOCKER_NETWORK optional: Docker-Netz fuer den Container (--network)
# ZAP_BASIC_AUTH_FILE optional: Datei AUSSERHALB des Repositorys mit einer Zeile
# benutzer:passwort. Nur noetig, falls das Ziel einmal vom
# Entwicklungsrechner aus eine Anmeldung (401) verlangt. Der
# Kopf geht ueber eine temporaere Umgebungsdatei (Modus 0600,
# wird beim Beenden entfernt) in den Container, nie auf eine
# Befehlszeile und in keine Ausgabe.
#
# Hinweis: Wird die Anmeldedatei benutzt, enthaelt der Bericht die gesendeten Kopfzeilen
# und damit die Zugangsdaten. Dann den Berichtsordner nicht weitergeben.
#
# Ergebnis: zap.html, zap.md, zap.json im Berichtsordner, dazu die Zeile
# ZAP-SUMMARY ziel=HOST hoch=N mittel=N niedrig=N info=N
# und je Meldungsart eine Zeile. Exit 0, wenn zap.json existiert, sonst 3 (damit der
# Freigabeschritt einen fehlenden Bericht bemerkt). Das Skript gibt kein Secret aus.
set -u
IMAGE='ghcr.io/zaproxy/zaproxy@sha256:7aaa659b0d43078febd82e29bad112285c370727e86ab8340444220e17d9f0d2'
TARGET="${ZAP_TARGET:-https://alpha.tessera.ctl.de}"
TARGET="${TARGET%/}"
MINUTES="${ZAP_SPIDER_MINUTES:-3}"
STAMP=$(date -u +%Y%m%d-%H%M%S)
REPORT_DIR="${ZAP_REPORT_DIR:-security-reports/zap-$STAMP}"
HOOK_IN_CONTAINER=/zap/hooks/zap-hooks.py
HERE=$(cd "$(dirname "$0")" && pwd)
HOOK_FILE="$HERE/zap-hooks.py"
# Host-Teil des Ziels fuer die Ausgabe (ohne Schema und Pfad)
HOST=${TARGET#*://}
HOST=${HOST%%/*}
ARGS="-t $TARGET -m $MINUTES -I -T 15 --autooff --hook=$HOOK_IN_CONTAINER -r zap.html -w zap.md -J zap.json"
if [ "${1:-}" = "--print-plan" ]; then
echo "ziel $TARGET"
echo "abbild $IMAGE"
echo "berichte $REPORT_DIR"
echo "zap-baseline.py $ARGS"
exit 0
fi
TMP_FILES=""
cleanup() {
for f in $TMP_FILES; do rm -f "$f"; done
}
trap cleanup EXIT HUP INT TERM PIPE
echo "ZAP-Grundpruefung gegen $TARGET (nur passiv, keine Formulare, keine Angriffe)"
# --- Vorabpruefung: Antwortet das Ziel, und verlangt es eine Anmeldung? ---------
CODE=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 20 "$TARGET/login" 2>/dev/null) || CODE=000
ENV_FILE=""
case "$CODE" in
200)
;;
401)
if [ -z "${ZAP_BASIC_AUTH_FILE:-}" ] || [ ! -r "${ZAP_BASIC_AUTH_FILE:-}" ]; then
echo "Das Ziel verlangt eine Anmeldung (Status 401)." >&2
echo "Legen Sie dafuer eine Datei ausserhalb des Repositorys an, mit einer Zeile" >&2
echo "benutzer:passwort, und rufen Sie das Skript mit ZAP_BASIC_AUTH_FILE=Pfad auf." >&2
exit 4
fi
CRED=$(head -n 1 "$ZAP_BASIC_AUTH_FILE" | tr -d '\r\n')
CURL_CFG=$(mktemp) || exit 4
ENV_FILE=$(mktemp) || exit 4
TMP_FILES="$CURL_CFG $ENV_FILE"
chmod 600 "$CURL_CFG" "$ENV_FILE"
printf 'user = "%s"\n' "$CRED" > "$CURL_CFG"
CODE2=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 20 -K "$CURL_CFG" "$TARGET/login" 2>/dev/null) || CODE2=000
if [ "$CODE2" != "200" ]; then
echo "Mit den hinterlegten Zugangsdaten antwortet das Ziel mit Status $CODE2, Abbruch." >&2
exit 4
fi
printf 'ZAP_BASIC_AUTH=Basic %s\n' "$(printf '%s' "$CRED" | base64 | tr -d '\n')" > "$ENV_FILE"
CRED=""
;;
*)
echo "Das Ziel antwortet auf /login nicht mit Status 200 (Status $CODE), Abbruch." >&2
echo "Pruefen Sie die Erreichbarkeit vom Entwicklungsrechner aus." >&2
exit 4
;;
esac
# --- Berichtsordner: der Container laeuft als Benutzer zap (Kennung 1000) ---------
mkdir -p "$REPORT_DIR" || exit 3
REPORT_DIR=$(cd "$REPORT_DIR" && pwd)
chmod 777 "$REPORT_DIR"
set -- docker run --rm
[ -n "${ZAP_DOCKER_NETWORK:-}" ] && set -- "$@" --network "$ZAP_DOCKER_NETWORK"
[ -n "$ENV_FILE" ] && set -- "$@" --env-file "$ENV_FILE"
set -- "$@" -v "$REPORT_DIR:/zap/wrk:rw" -v "$HOOK_FILE:$HOOK_IN_CONTAINER:ro" "$IMAGE" \
zap-baseline.py -t "$TARGET" -m "$MINUTES" -I -T 15 --autooff "--hook=$HOOK_IN_CONTAINER" \
-r zap.html -w zap.md -J zap.json
# Exit-Codes von ZAP (0 ok, 1 Fehler, 2 Warnung, 3 Fehlstart) sind hier nicht
# entscheidend: es zaehlt, ob der Bericht entstanden ist.
"$@" > "$REPORT_DIR/zap-lauf.log" 2>&1
# Die Umgebungsdatei mit dem Kopf wird nur fuer den Lauf gebraucht: sofort entfernen.
cleanup
TMP_FILES=""
echo "ZAP-Lauf beendet (Protokoll: $REPORT_DIR/zap-lauf.log)"
if [ ! -s "$REPORT_DIR/zap.json" ]; then
echo "Es ist kein Bericht entstanden (zap.json fehlt)." >&2
tail -n 15 "$REPORT_DIR/zap-lauf.log" >&2
exit 3
fi
python3 -I - "$REPORT_DIR/zap.json" "$HOST" <<'PY'
import json, sys
path, host = sys.argv[1], sys.argv[2]
with open(path, encoding="utf-8") as fh:
data = json.load(fh)
seen = {}
for site in data.get("site", []):
for alert in site.get("alerts", []):
key = (alert.get("pluginid"), alert.get("name"))
seen[key] = int(alert.get("riskcode", 0))
count = {3: 0, 2: 0, 1: 0, 0: 0}
for risk in seen.values():
count[risk] = count.get(risk, 0) + 1
print("ZAP-SUMMARY ziel=%s hoch=%d mittel=%d niedrig=%d info=%d"
% (host, count[3], count[2], count[1], count[0]))
word = {3: "hoch", 2: "mittel", 1: "niedrig", 0: "info"}
for (_pid, name), risk in sorted(seen.items(), key=lambda kv: (-kv[1], str(kv[0][1]))):
print("ZAP-MELDUNG %s %s" % (word.get(risk, str(risk)), name))
PY
echo "Berichte: $REPORT_DIR (zap.html, zap.md, zap.json)"
exit 0
+32
View File
@@ -0,0 +1,32 @@
"""zap-hooks.py -- Haken fuer die ZAP-Grundpruefung (quick-261009-p0m).
Wird von .gitea/scripts/zap-baseline.sh schreibgeschuetzt in den ZAP-Container
eingebunden und mit `--hook=` geladen. Nur Standardbibliothek.
Aufgaben:
1. Die klassische Spinne soll Formulare weder ausfuellen noch absenden. So bleibt
die Pruefung rein passiv: es werden nur Seiten abgerufen (GET), nie Daten
an die Anwendung geschickt (kein POST).
2. Nur wenn die Umgebungsvariable ZAP_BASIC_AUTH gesetzt ist (Anmeldedatei-Weg des
Skripts), wird ein Ersetzungsregel fuer den Kopf Authorization eingetragen.
Der Wert steht nie in einem Aufruf oder einer Ausgabe.
"""
import os
def zap_started(zap, target):
# Keine Formulare verarbeiten, keine Formulare absenden.
zap.spider.set_option_process_form(False)
zap.spider.set_option_post_form(False)
auth = os.environ.get("ZAP_BASIC_AUTH")
if auth:
zap.replacer.add_rule(
description="basicauth",
enabled=True,
matchtype="REQ_HEADER",
matchstring="Authorization",
matchregex=False,
replacement=auth,
initiators="",
)
+1 -1
View File
@@ -6,7 +6,7 @@ Diese Liste beschreibt in einfachen Worten, was sich von Version zu Version an T
### Neu
- Sicherheit: Neu ist ein Sicherheitsprotokoll, das in einfachen Worten festhält, was an Tessera bisher auf Sicherheit geprüft wurde – alle Code-Prüfungen, die Schutzmaßnahmen und die erste vollständige Prüfung vom 9. Oktober 2026 samt Bewertung jedes Befunds – und das bei jeder weiteren Prüfung fortgeschrieben wird. Außerdem prüft die Plattform nach jedem Bau automatisch den Quelltext, seine gesamte Geschichte und die fertigen Pakete auf Zugangsdaten und bekannte Schwachstellen in verwendeten Bausteinen. Diese Prüfung meldet nur und hält einen Bau nie an.
- Sicherheit: Neu ist ein Sicherheitsprotokoll, das in einfachen Worten festhält, was an Tessera bisher auf Sicherheit geprüft wurde – alle Code-Prüfungen, die Schutzmaßnahmen und die erste vollständige Prüfung vom 9. Oktober 2026 samt Bewertung jedes Befunds – und das bei jeder weiteren Prüfung fortgeschrieben wird. Außerdem prüft die Plattform nach jedem Bau automatisch den Quelltext, seine gesamte Geschichte und die fertigen Pakete auf Zugangsdaten und bekannte Schwachstellen in verwendeten Bausteinen. Diese Prüfung meldet nur und hält einen Bau nie an. Vor jeder Freigabe wird der Testserver alpha zusätzlich von außen angesehen, so wie ein Besucher ohne Konto: rein passiv, ohne Formulare abzusenden und ohne Angriffe; das Ergebnis jedes Laufs steht im Sicherheitsprotokoll.
- Eigene Module beim Start vorladen: Jedes eigene Modul – auch Einträge, die ein Administrator für alle angelegt hat – lässt sich auf „Beim Start vorladen“ stellen, in der Leiste oben in der Modulansicht oder unter Einstellungen → Eigene Module. Tessera lädt solche Module dann kurz nach dem Start nacheinander unsichtbar im Hintergrund, sodass der erste Klick sofort die fertige Seite zeigt. Die Wahl gilt nur für den jeweiligen Benutzer, im Browser und in der Desktop-App gleich; vorladen lassen sich höchstens acht Module, beim neunten erscheint ein Hinweis.
- Marktplatz: Die Detailseite jedes Moduls zeigt jetzt unter „Änderungen“, was sich von Modulversion zu Modulversion geändert hat – die neueste Version oben, ältere zum Aufklappen. Die Versionsnummern aller Module wurden dabei rückwirkend nachgetragen.
- Neues Modul „Domains“ in der Gruppe „Domains“: Domains bei AutoDNS registrieren, Kontakte und Kunden zuordnen. Ein Administrator aktiviert das Modul im Marktplatz; wer es nutzen soll, bekommt zusätzlich die Freigabe. Mit „Benutzen“ sehen Sie die Domainliste, die Kontakte, die Kunden und die Aufträge; mit „Verwalten“ (und als Administrator) registrieren Sie Domains, legen Kontakte und Kunden an und richten die Anbindung ein.
+14 -1
View File
@@ -534,7 +534,20 @@ was dabei passiert:
Freigabedatum. Dann wird in `CHANGELOG.md` der Abschnitt „Unveröffentlicht“ in
„X.Y.Z – JJJJ-MM-TT“ umbenannt, darüber ein neues, leeres „Unveröffentlicht“
angelegt, und das Ganze auf `main` committet und gepusht.
Erst dann wird zusammengeführt und getaggt:
2. **Prüfung von außen:** Sobald alpha den Beta-Stand zeigt, der freigegeben
werden soll (die Versionszeile unter `/health/version`, siehe unten, muss
stimmen), führt Claude vom Entwicklungsrechner aus die passive Grundprüfung
aus: `sh .gitea/scripts/zap-baseline.sh`. Sie sieht sich alpha an wie ein
Besucher ohne Konto, füllt keine Formulare aus und greift nichts an. Als Ziel
gilt die öffentliche Adresse von alpha. Ist sie vom Entwicklungsrechner nicht
erreichbar, wird mit `ZAP_TARGET` die direkte Adresse der Anwendung auf dem
Testserver angegeben; das Ergebnis vermerkt dann, dass der vorgeschaltete Proxy
nicht mitgeprüft wurde. Der Zugangsschutz (Basic Auth) vor alpha bleibt
unverändert bestehen. Das Ergebnis trägt Claude im
[Sicherheitsprotokoll](sicherheitsprotokoll.md) ein (Eintrag unter „Verlauf“,
neue Befunde in „Einordnung der Befunde“). Ein neuer Befund der Stufe „Hoch“
wird vor der Freigabe behoben oder im Protokoll begründet.
3. **Zusammenführen und taggen:** Erst dann wird zusammengeführt und getaggt:
```bash
git checkout live
+32
View File
@@ -907,6 +907,38 @@ Code-Analyse). Die Regeln:
- Jeder Eintrag in `.gitleaks.toml` trägt eine deutsche `description`, die sagt, warum es ein Fehlalarm ist.
- Nie ganze Verzeichnisse außerhalb der Ordner mit Testdaten freigeben.
### Prüfung von außen (ZAP)
Vor einer Freigabe wird alpha mit dem OWASP-ZAP-Abbild von außen angesehen (Ablauf: Betriebsanleitung, Kapitel 9, „Eine
Version freigeben“). Vom Hauptordner des Repositorys aus:
```bash
sh .gitea/scripts/zap-baseline.sh --print-plan # zeigt Ziel, Abbild und Aufruf, ohne Netz
sh .gitea/scripts/zap-baseline.sh # Lauf gegen https://alpha.tessera.ctl.de
ZAP_TARGET=http://HOST:3000 sh .gitea/scripts/zap-baseline.sh # direkt gegen die Anwendung, ohne Proxy
```
Umgebungsvariablen: `ZAP_TARGET` (Ziel; Standard ist die öffentliche Adresse von alpha), `ZAP_SPIDER_MINUTES` (Höchstdauer der
Spinne, Standard 3), `ZAP_REPORT_DIR` (Standard `security-reports/zap-{Zeit}`), `ZAP_DOCKER_NETWORK` (optional, Docker-Netz
des Containers) und `ZAP_BASIC_AUTH_FILE` (siehe unten). Ergebnis sind `zap.html`, `zap.md` und `zap.json` im Berichtsordner
sowie die Zeile `ZAP-SUMMARY ziel=… hoch=… mittel=… niedrig=… info=…` mit je einer Zeile `ZAP-MELDUNG` pro Meldungsart. Das
Skript endet mit Exit 0, wenn der Bericht entstanden ist, und mit 3, wenn nicht; so bemerkt der Freigabeschritt einen
fehlenden Bericht. Es prüft vorab `GET /login` des Ziels: Antwortet es nicht mit 200, bricht es mit einer deutschen Meldung ab.
**Warum nur passiv.** Das Skript nutzt ausschließlich die klassische Spinne, die Links folgt. Den AJAX-Spider gibt es nicht
im Aufruf, weil er Formulare der Seite ausfüllen und absenden würde, und für Formulare schaltet der Haken
`.gitea/scripts/zap-hooks.py` die Verarbeitung und das Absenden der Spinne ausdrücklich ab. Aktive Regeln laufen nicht. Der
Beweis, dass dabei kein POST entsteht, wurde vor dem ersten Lauf gegen einen lokalen Testserver mit Anmeldeformular geführt
(0 POST bei allen Anfragen). Hinweis: ZAP 2.17 ignoriert bei diesem Aufruf die Option `-z`; deshalb läuft die Einstellung
über den Haken.
**Anmeldedatei für den Ausnahmefall.** Der Zugangsschutz (Basic Auth) vor alpha bleibt bestehen. Fragt alpha den Entwicklungsrechner
einmal doch nach einer Anmeldung (die Vorabprüfung meldet dann Status 401), legen Sie eine Datei **außerhalb des
Repositorys** mit einer Zeile `benutzer:passwort` an und rufen das Skript mit `ZAP_BASIC_AUTH_FILE=Pfad` auf. Der Kopf
gelangt über eine temporäre Datei mit Modus 0600 (wird beim Beenden entfernt) in den Container und nie auf eine Befehlszeile
oder in eine Ausgabe. Achtung: Der Bericht enthält dann die gesendeten Kopfzeilen und damit die Zugangsdaten; geben Sie ihn
nicht weiter. Der Ordner `security-reports/` ist von Git ausgeschlossen.
## Konventionen und Fallstricke
**NestJS-Routenreihenfolge:** NestJS matcht Routen in Deklarationsreihenfolge. Eine statische Route
+39
View File
@@ -14,6 +14,7 @@ Das Protokoll enthält bewusst keine Passwörter, keine Zugangsdaten und keine A
- **Fremde Bausteine:** In den Bausteinen anderer Hersteller, die Tessera im Betrieb verwendet, sind 151 bekannte Schwachstellen verzeichnet, davon 5 kritische und 73 hohe. Für die allermeisten gibt es eine bereinigte Fassung innerhalb der aktuell verwendeten Versionslinie. Für zwei Bausteine (xlsx und node-forge) gibt es noch keine bereinigte Fassung; sie sind unter „Einordnung der Befunde“ eingeordnet.
- **Eigener Quelltext:** Ein einziger echter Härtungspunkt wurde gefunden (die Längenprüfung bei der Entschlüsselung gespeicherter Geheimnisse, Schweregrad niedrig). Alles andere sind Fehlalarme oder bewusste, dokumentierte Entscheidungen.
- **Automatische Prüfung:** Seit dem 9. Oktober 2026 läuft nach jedem Bau der Plattform automatisch eine Sicherheitsprüfung. Sie meldet nur und hält den Bau nie an.
- **Prüfung von außen:** Vor jeder Freigabe wird der Testserver alpha wie ein Besucher ohne Konto angesehen, rein passiv und ohne Angriffe. Der erste Lauf am 9. Oktober 2026 (direkt gegen die Anwendung, ohne den vorgeschalteten Proxy) fand nichts Hohes, 2 mittlere und 6 niedrige Hinweise sowie 3 reine Informationen; alle betreffen fehlende Schutz-Kopfzeilen der Weboberfläche. Der Lauf über die öffentliche Adresse steht noch aus.
- **Bisherige Prüfungen:** Acht Code-Prüfungen größerer Änderungen mit zusammen 89 Befunden; 76 davon sind behoben, die übrigen 13 sind Hinweise der niedrigsten Stufe. Alle kritischen Befunde und alle Warnungen sind behoben.
- **Was noch zu tun ist:** Die Bausteine mit bereinigter Fassung werden aktualisiert, und die fertigen Abbilder sollen keine Entwicklungswerkzeuge mehr enthalten. Der Stand jedes einzelnen Punkts steht in der Tabelle „Einordnung der Befunde“.
@@ -70,6 +71,12 @@ Semgrep liest den von uns geschriebenen Quelltext und sucht nach Mustern, die er
Zum Schluss prüft Trivy die beiden fertig gebauten Abbilder, `api` und `web`, so wie sie auf einem Server laufen würden. Dabei zählt es auch die Pakete des Betriebssystems im Abbild und alles, was im Abbild zusätzlich steckt, etwa Entwicklungswerkzeuge. Deshalb liegen die Zahlen hier über denen der Paketlisten.
### Prüfung von außen vor einer Freigabe (OWASP ZAP)
Vor jeder Freigabe sieht sich Claude den Testserver alpha von außen an, so wie ein Besucher ohne Konto: Startseite, Anmeldeseite, die mitgelieferten Kopfzeilen, Cookies und die ausgelieferten Dateien. Dafür wird das frei verfügbare Werkzeug OWASP ZAP in einem Container gestartet. Es arbeitet **nur passiv**: Es ruft Seiten ab und wertet aus, was der Server zurückschickt. Es füllt keine Formulare aus, sendet keine Daten an die Anwendung und probiert keine Angriffe aus. Ein Probelauf gegen eine eigens dafür gebaute Testseite mit Anmeldeformular hat bewiesen, dass dabei kein einziges Absenden (POST) stattfindet.
Der Zugangsschutz (Basic Auth) vor alpha bleibt, wie er ist. Die Prüfung läuft vom internen Entwicklungsrechner aus, für den der Zugangsschutz keine Anmeldung verlangt. Falls alpha diesen Rechner einmal doch nach einer Anmeldung fragt, kann das Skript die Zugangsdaten aus einer Datei außerhalb des Projekts lesen. Das Ergebnis jedes Laufs wird in diesem Protokoll festgehalten (Abschnitt „Prüfung von außen gegen alpha“ und „Verlauf“). Den Ablauf beschreibt der Schritt „Eine Version freigeben“ in der [Betriebsanleitung](anleitung-betrieb.md#9-zwei-kanäle-live-und-beta); die technischen Einzelheiten stehen in der [Entwicklungsanleitung](anleitung-entwicklung.md#sicherheitsprüfungen).
### Prüfung durch Menschen und Künstliche Intelligenz
Neben den Werkzeugen gibt es Prüfungen, die durch Lesen und Nachdenken entstehen:
@@ -164,6 +171,7 @@ Die Tabelle zeigt für jedes Werkzeug zwei Zahlen. „Rohwert“ ist der erste L
| Eigener Quelltext (Semgrep) | 58 Treffer (9 Fehler, 46 Warnungen, 3 mittlere); 59 Scanfehler | 34 Treffer (2 Fehler, 29 Warnungen, 3 mittlere); 4 Scanfehler | Die Treffer sind unter „Einordnung der Befunde“ beurteilt: ein echter Härtungspunkt, sonst Fehlalarme und Entscheidungen. Die Ausnahmeliste nahm Testschlüssel, Tests und Notizen heraus |
| Abbild `api` (Trivy) | Bausteine: 6 kritisch, 116 hoch, 98 mittel, 7 niedrig; Betriebssystem 1 mittel | 6 kritisch, 116 hoch, 99 mittel, 7 niedrig | Das Server-Abbild enthält Entwicklungswerkzeuge und einen Paketverwalter, die im Betrieb nichts tun, aber mitgezählt werden |
| Abbild `web` (Trivy) | Bausteine: 2 kritisch, 19 hoch, 21 mittel, 1 niedrig; Betriebssystem 1 mittel | 2 kritisch, 19 hoch, 22 mittel, 1 niedrig | Die kritischen Funde sind dieselben des Webframeworks wie oben; dazu kommen Pakete des mitgelieferten Paketverwalters npm |
| Prüfung von außen (ZAP, direkt gegen die Anwendung auf alpha) | 0 hoch, 2 mittel, 6 niedrig, 3 Informationen | keine Ausnahmelisten | Nur fehlende Schutz-Kopfzeilen der Weboberfläche und Hinweise zum Zwischenspeichern; Einzelheiten im Abschnitt „Prüfung von außen gegen alpha“ |
**Gegenprobe:** Um zu beweisen, dass die Ausnahmelisten nichts Echtes verdecken, wurde in einer Wegwerfkopie des Projekts ein erfundener Zugangscode (ein „Köder“) abgelegt. gitleaks und Trivy haben ihn gefunden. Die Kopie und der Köder wurden danach gelöscht.
@@ -199,11 +207,42 @@ Die Tabelle beurteilt jeden Befund der ersten vollständigen Prüfung. Sie wird
| Einstellungen zum Absichern der Paketverwaltung (drei Werte) | Arbeitsbereich-Datei | Niedrig | offen | Diese Einstellungen stärken die Lieferkette. Ob sie in der eingesetzten Fassung 9.15 des Paketverwalters wirken, ist noch nicht geprüft |
| Fehlende Gesundheitsprüfung im Bauplan des Server-Abbilds | Abbild `api` | Niedrig | bewusst akzeptiert | Die Gesundheitsprüfung liegt in der Compose-Datei, die den Server startet und seine Erreichbarkeit überwacht |
| Fehlende Gesundheitsprüfung im Bauplan des Web-Abbilds | Abbild `web` | Niedrig | offen | Weder Bauplan noch Compose-Datei prüfen die Weboberfläche; ein hängender Web-Container würde nicht automatisch erkannt |
| Fehlende Inhaltsrichtlinie und fehlender Schutz gegen Einrahmen (Außenprüfung 9. Oktober 2026) | Weboberfläche, alle Seiten | Mittel | offen | Die Anwendung selbst schickt diese Kopfzeilen bisher nicht mit. Ein vorgeschalteter Proxy kann sie ergänzen; ob er das tut, kann nur der Lauf über die öffentliche Adresse zeigen. Aufgabe des Proxys, beim öffentlichen Lauf erneut prüfen. Fehlen sie dort auch, werden sie in der Anwendung gesetzt. Das tatsächliche Risiko ist gering bis mittel: Es braucht eine fremde Seite, die Benutzer zu Klicks verleitet, oder bereits eingeschleusten Code |
| Weitere fehlende Schutz-Kopfzeilen (Dateityp-Umdeutung, Berechtigungsrichtlinie, drei Cross-Origin-Richtlinien) | Weboberfläche | Niedrig | offen | Reine Vorsorge ohne unmittelbare Gefahr. Teilweise Aufgabe des Proxys (beim öffentlichen Lauf erneut prüfen), sonst kommen sie in die Konfiguration der Weboberfläche |
| Kopfzeile „X-Powered-By“ nennt das Webframework | Weboberfläche | Niedrig | offen | Das Webframework schickt diese Angabe standardmäßig mit. Sie verrät nur den Namen des Frameworks, den Angreifer ohnehin leicht erraten. Wird bei der Härtung der Kopfzeilen mit abgeschaltet |
| Verschlüsselte Verbindung (HTTPS) und deren Absicherung der öffentlichen Adresse | Proxy vor alpha | nicht beurteilt | offen | Der erste Lauf ging über eine unverschlüsselte, interne Verbindung direkt zur Anwendung und konnte das nicht sehen. Aufgabe des Proxys, beim öffentlichen Lauf erneut prüfen |
| Drei Informationen der Außenprüfung (Dateityp bei Umleitungen, Zwischenspeicherung) | Weboberfläche | Information | bewusst akzeptiert | Es sind Beschreibungen, keine Schwächen: Anmeldeseiten sind nicht speicherbar, die öffentlichen Programmdateien sind es. Daran ist nichts zu ändern |
## Prüfung von außen gegen alpha
Jede Zeile ist ein Lauf der passiven Außenprüfung (OWASP ZAP 2.17.0). Gezählt werden Meldungsarten, nicht einzelne Fundstellen; dieselbe Meldung auf mehreren Seiten zählt einmal.
| Datum | Version | Weg | Hoch | Mittel | Niedrig | Info |
|-------|---------|-----|------|--------|---------|------|
| 9. Oktober 2026 | v1.10.1-80-gd15a470 (Beta) | direkt gegen die Anwendung, ohne Proxy | 0 | 2 | 6 | 3 |
**Zum ersten Lauf.** Die öffentliche Adresse von alpha war an diesem Tag vom Prüfrechner aus nicht erreichbar (die Verbindung lief in eine Zeitüberschreitung). Der erste Lauf ging deshalb direkt gegen die Anwendung auf alpha, über die interne Adresse des Testservers und ohne den vorgeschalteten Proxy. Das ist ein Lauf mit denselben Regeln (passiv, keine Formulare, keine Angriffe), aber er sieht nur, was die Anwendung selbst ausliefert. Alles, was der Proxy beisteuert, etwa die verschlüsselte Verbindung (HTTPS) und deren Absicherung, konnte dieser Lauf nicht beurteilen. **Der Lauf über die öffentliche Adresse folgt, sobald diese vom Prüfrechner erreichbar ist.** Gefunden wurden 28 Seiten und Dateien; es gab keine Anmeldung und keine Datenänderung.
Was die einzelnen Meldungen bedeuten:
- **Fehlende Inhaltsrichtlinie (Content Security Policy), mittel.** Mit dieser Kopfzeile sagt eine Webseite dem Browser, von wo sie Skripte und andere Inhalte laden darf. Sie bremst Angriffe, bei denen fremder Code in eine Seite eingeschleust wird. Ohne sie gilt keine solche Einschränkung. Es ist eine fehlende Vorsorge, keine ausgenutzte Lücke.
- **Fehlender Schutz gegen Einrahmen (Clickjacking), mittel.** Ohne passende Kopfzeile könnte eine fremde Seite die Anmeldeseite unsichtbar in einen Rahmen legen und Besucher zu Klicks verleiten. Zu sehen war das auf der Anmeldeseite und der Seite zum Zurücksetzen des Passworts.
- **Fehlende Kopfzeile gegen das Umdeuten von Dateitypen, niedrig.** Sie hindert Browser daran, eine Datei als etwas anderes zu behandeln, als der Server sagt.
- **Fehlende Berechtigungsrichtlinie, niedrig.** Damit lässt sich festlegen, auf welche Geräte-Funktionen (zum Beispiel Kamera oder Standort) eine Seite zugreifen darf.
- **Drei fehlende Kopfzeilen zur Trennung von Seiten untereinander (Cross-Origin-Embedder-, -Opener- und -Resource-Policy), niedrig.** Sie sollen verhindern, dass fremde Seiten Inhalte der Anwendung einbinden oder beobachten. Für eine Anwendung ohne eingebettete Fremdinhalte ist der Nutzen klein.
- **Kopfzeile „X-Powered-By“, niedrig.** Der Server nennt das Webframework, mit dem er gebaut ist. Das hilft einem Angreifer nur beim Raten, welche Schwachstellen er probieren könnte.
- **Drei Informationen.** „Content-Type fehlt“ trifft Umleitungen und leere Antworten ohne Inhalt. „Nicht speicherbar“ und „speicherbar und zwischenspeicherbar“ beschreiben nur, ob Browser und Zwischenspeicher eine Antwort behalten dürfen: Anmeldeseiten sind zu Recht nicht speicherbar, die öffentlichen Programmdateien sind es.
Die Beurteilung jeder Meldung steht in der Tabelle „Einordnung der Befunde“.
## Verlauf
Der neueste Eintrag steht oben.
### 2026-10-09 — Erste Prüfung von außen gegen alpha (OWASP ZAP)
Passiver Erstlauf gegen alpha in der Fassung v1.10.1-80-gd15a470 (Beta), direkt gegen die Anwendung und ohne den vorgeschalteten Proxy, weil die öffentliche Adresse vom Prüfrechner nicht erreichbar war. Ergebnis: 0 hoch, 2 mittel, 6 niedrig, 3 Informationen; alle Meldungen betreffen fehlende Schutz-Kopfzeilen der Weboberfläche oder beschreiben Zwischenspeicherung. Vor dem Lauf bewies ein Probelauf gegen eine Testseite, dass das Skript keine Formulare absendet. Beurteilung: siehe „Prüfung von außen gegen alpha“ und „Einordnung der Befunde“. **Offen:** der Lauf über die öffentliche Adresse; dort werden auch die Kopfzeilen und die Verschlüsselung des Proxys sichtbar.
### 2026-10-09 — Erste vollständige Prüfung und automatische Prüfung eingerichtet
Zum ersten Mal wurde das gesamte Projekt mit allen fünf Werkzeugen geprüft: Zugangsdaten in der Geschichte, Bausteine anderer Hersteller, eigener Quelltext und die beiden fertigen Abbilder. Ergebnis: keine echten Geheimnisse, 151 bekannte Schwachstellen in Bausteinen im Betrieb (5 kritisch, 73 hoch), ein echter Härtungspunkt im eigenen Quelltext und ein Server-Abbild, das Entwicklungswerkzeuge enthält. Die Einzelheiten stehen in den Abschnitten „Erste vollständige Prüfung“ und „Einordnung der Befunde“. Gleichzeitig wurde die automatische Sicherheitsprüfung nach jedem Bau eingerichtet und mit einer Gegenprobe (erfundener Köder-Code) im echten Bau-Umfeld geprüft; sie meldet nur und hält nie etwas an. Danach wurde die Liste der Ausnahmen für geprüfte Fehlalarme angelegt.