integrity_scanner_fuer_stat.../BEDIENUNGSANLEITUNG.md
Your Name 5769f3fc88 docs: Bedienungsanleitung hinzufügen
Vollständige Bedienungsanleitung für den täglichen Betrieb, Erstinbetriebnahme,
Alarm-Workflows, Cron-Setup, E-Mail-Konfiguration und Konfigurationsreferenz.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 03:28:36 +02:00

17 KiB
Raw Blame History

Bedienungsanleitung — Website Integrity Scanner

Überwachungswerkzeug für die statische Website bredelar.info gegen SEO-Spam und unbefugte Manipulationen. Der Scanner läuft lokal im Verantwortungsbereich des Betreibers.


Inhaltsverzeichnis

  1. Grundprinzip
  2. Voraussetzungen und Installation
  3. Erstinbetriebnahme
  4. Täglicher Betrieb
  5. Befehle im Überblick
  6. Ergebnis-Level und Exit-Codes
  7. Berichte lesen
  8. Änderungen freigeben (approve)
  9. Vorgehen bei einem Alarm
  10. Automatisierung per Cron-Job
  11. E-Mail-Benachrichtigung einrichten
  12. Konfigurationsreferenz
  13. Externe Domains verwalten
  14. Rausch-Unterdrückung konfigurieren
  15. Was der Scanner erkennt
  16. Verzeichnisstruktur

1. Grundprinzip

Der Scanner arbeitet nach dem Baseline-Diff-Prinzip:

Initiale Sichtprüfung → Baseline festlegen
         ↓
    Täglicher Scan
         ↓
  Aktueller Zustand  ──vergleiche──  genehmigte Baseline
         ↓
   Abweichungen → Score → GREEN / YELLOW / RED
  • Die Baseline ist ein einmal manuell geprüfter und freigegebener Referenzzustand. Sie wird niemals automatisch aktualisiert.
  • Jede Abweichung erhält Punkte. Der Gesamt-Score entscheidet über das Alarm-Level.
  • Legitime Änderungen (z.B. neuer Artikel) müssen explizit per approve freigegeben werden.

2. Voraussetzungen und Installation

# Python 3.10 oder neuer erforderlich
python3 --version

# Virtuelle Umgebung anlegen und aktivieren
cd /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten
python3 -m venv venv
source venv/bin/activate

# Abhängigkeiten installieren
pip install -r requirements.txt

Alle Befehle müssen aus dem Projektverzeichnis ausgeführt werden (dort liegt config.yaml).


3. Erstinbetriebnahme

Schritt 1: Ersten Crawl durchführen

python -m scanner init

Die Ausgabe zeigt:

  • alle gefundenen internen URLs
  • alle externen Domains (mit Hinweis, ob in der Whitelist)
  • potenzielle Auffälligkeiten (Hidden Content, Meta-Refresh, verdächtige Scripts)
  • Crawl-Fehler (Seiten, die nicht erreichbar waren)

Schritt 2: Ausgabe manuell prüfen

Wichtig vor dem nächsten Schritt:

  • Alle externen Domains prüfen. Legitime Domains in config/allowed_external.yaml eintragen (siehe Abschnitt 13).
  • Auffälligkeiten im Browser nachschauen.

Schritt 3: Baseline freigeben

Nur wenn alles sauber ist:

python -m scanner approve --all --note "Initiale Baseline, geprüft am $(date +%Y-%m-%d)"

Ab jetzt ist der tägliche Betrieb möglich.


4. Täglicher Betrieb

Manueller Scan

python -m scanner scan

Ergebnis erscheint direkt im Terminal. Der vollständige Report liegt in reports/.

Automatischer Scan (Cron)

# Cron-Eintrag hinzufügen
crontab -e

Eintrag:

# Website-Scan täglich um 03:00 Uhr
0 3 * * * cd /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten && /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten/venv/bin/python -m scanner scan >> logs/cron.log 2>&1

Der Exit-Code (0/1/2) ist für Cron-basierte Alarmierung nutzbar (z.B. mit cronitor oder systemd-Timern mit OnFailure=).


5. Befehle im Überblick

Alle Befehle werden mit python -m scanner <befehl> aufgerufen. Globale Optionen: --config path/to/config.yaml, --verbose (-v).

Befehl Zweck
init Erstcrawl, Ausgabe aller Funde, noch kein Baseline-Update
crawl Crawlt die Website, speichert Snapshot (kein Vergleich)
check Vergleicht letzten Snapshot mit Baseline, erstellt Report
scan crawl + check in einem Schritt (für den Cron-Betrieb)
approve --all Alle neuen Snapshot-URLs in die Baseline übernehmen
approve --url URL Einzelne URL freigeben
approve --rebuild Gesamte Baseline ersetzen (nach großem Redesign)
report Letzten Report im Terminal anzeigen
report --format json Letzten Report als JSON anzeigen
status Baseline-Datum, letzter Scan, aktueller Risk-Level

Beispiele

# Einzelne Seite freigeben (z.B. nach Impressums-Update)
python -m scanner approve --url https://bredelar.info/impressum/ --note "Impressum aktualisiert Juni 2026"

# Alle Änderungen auf einmal freigeben (nur nach vollständiger Prüfung!)
python -m scanner approve --all --note "Redesign vom 2026-06-12 freigegeben"

# Baseline nach komplettem Umbau neu aufbauen
python -m scanner approve --rebuild --note "Komplettes Redesign v2, alle Seiten neu geprüft"

# Bestimmten Snapshot freigeben (statt den neuesten)
python -m scanner approve --all --snapshot data/snapshots/20260612_030000

# Aktuellen Stand prüfen
python -m scanner status

# Letzten Report anzeigen
python -m scanner report

6. Ergebnis-Level und Exit-Codes

Level Score Exit-Code Bedeutung
GREEN 019 0 Keine Auffälligkeiten
YELLOW 2059 1 Warnung — manuelle Prüfung empfohlen
RED ≥ 60 2 Alarm — sofortige Prüfung notwendig

Die Schwellenwerte sind in config.yaml unter thresholds anpassbar.


7. Berichte lesen

Reports liegen in reports/YYYYMMDD_HHMMSS/ als report.md und report.json.

# Neuesten Report im Terminal lesen
python -m scanner report

# Direkt die Datei öffnen
ls reports/
cat reports/20260612_030000/report.md

Aufbau des Reports

Abschnitt Inhalt
Zusammenfassung Kennzahlen auf einen Blick
Risikobewertung Alle Gründe für den Score (was hat wie viele Punkte ausgelöst)
Neue externe Domains Domains, die bisher nicht in der Baseline waren
Neue interne URLs Seiten, die neu dazugekommen sind
Fehlende URLs Seiten, die in der Baseline stehen, jetzt aber nicht mehr gefunden wurden
Geänderte Seiten Detailansicht: Textänderungen, Link-Diffs, Meta-Änderungen
Kaputte Links [NEU] = noch nicht in Baseline, [bekannt] = schon bekannt, wird nicht bewertet
Unerwartetes JSON-LD Strukturierte Daten mit unbekanntem @type
Verdächtige Dateinamen Links auf Dateien mit Webshell-typischen Namen
Whitelist-Verstösse Externe Domains, die nicht in allowed_external.yaml stehen
Fehlende Security-Header Informativ (kein Score) — nur auf HTTP-200-Seiten
Crawl-Fehler URLs, die der Crawler nicht laden konnte
Nächste Schritte Abhängig vom Level: konkrete Handlungsempfehlungen

8. Änderungen freigeben (approve)

Wann approve nötig ist:

  • Nach einer legitimen Inhaltsänderung (neuer Artikel, aktualisiertes Impressum, ...)
  • Nach einem Redesign
  • Nach Bereinigung eines echten Angriffs

Einzelne URL freigeben

Wenn nur eine bestimmte Seite geändert wurde:

python -m scanner approve --url https://bredelar.info/veranstaltungen/ \
  --note "Veranstaltungskalender aktualisiert"

Alle Änderungen freigeben

Nach Prüfung aller markierten URLs:

python -m scanner approve --all --note "Monatliches Update geprüft und freigegeben"

Achtung: --all übernimmt alles aus dem letzten Snapshot. Nur anwenden, wenn jede gemeldete Änderung manuell gesichtet wurde.

Komplette Baseline neu aufbauen

Nach einem großen Redesign oder nach Bereinigung eines Angriffs:

python -m scanner approve --rebuild --note "Nach Angriff bereinigt, neues Redesign"

--rebuild ersetzt die gesamte bestehende Baseline durch den aktuellen Snapshot.


9. Vorgehen bei einem Alarm

YELLOW — Warnung

  1. Report lesen: python -m scanner report
  2. Markierte URLs im Browser aufrufen und Quelltext prüfen
  3. Ist die Änderung legitim?
    • Ja: python -m scanner approve --url <URL> --note "Grund"
    • Nein: Abschnitt Angriff lesen

RED — Alarm

Der Score ist ≥ 60. Typische Auslöser: neue externe Domain (50 Pkt.) + Hidden Content (40 Pkt.), Webshell-Dateiname (60 Pkt.), Meta-Refresh (40 Pkt.).

Angriff

  1. Nicht sofort approve aufrufen — das würde den kompromittierten Zustand als neue Baseline festschreiben.
  2. Report sichern: cp -r reports/$(ls reports/ | tail -1) ~/alarm-$(date +%Y%m%d)/
  3. Website im Browser aufrufen und nach sichtbaren Fremdinhalten suchen.
  4. Quelltext der betroffenen Seiten prüfen (insbesondere display:none-Bereiche).
  5. Hoster kontaktieren, SSH-/FTP-Zugriffslogs prüfen.
  6. Dateien mit Backup vergleichen (diff oder rsync --dry-run).
  7. Nach vollständiger Bereinigung:
    python -m scanner scan          # muss GREEN ergeben
    python -m scanner approve --rebuild --note "Nach Angriff bereinigt $(date +%Y-%m-%d)"
    

10. Automatisierung per Cron-Job

Cron-Eintrag

crontab -e
# Website-Scan täglich um 03:00 Uhr
0 3 * * * cd /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten && /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten/venv/bin/python -m scanner scan >> logs/cron.log 2>&1

Log prüfen

tail -50 logs/cron.log
tail -f logs/scanner.log        # detaillierteres Log des Scanners selbst

Log-Rotation (optional)

Datei /etc/logrotate.d/integrity-scanner:

/home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten/logs/*.log {
    daily
    rotate 30
    compress
    missingok
    notifempty
}

11. E-Mail-Benachrichtigung einrichten

config.yaml anpassen

alerting:
  min_level: "yellow"   # oder "red" — nur bei rotem Alarm mailen
  email:
    enabled: true
    smtp_host: "mail.linix.de"      # SMTP-Server des Providers
    smtp_port: 587
    smtp_tls: true
    smtp_user: "scanner@linix.de"
    smtp_password_env: "SCANNER_SMTP_PASSWORD"
    from: "scanner@bredelar.info"
    to:
      - "dieter.schlueter@linix.de"

Passwort als Umgebungsvariable setzen

Niemals das Passwort direkt in config.yaml eintragen. Stattdessen:

# Temporär (bis zum nächsten Login)
export SCANNER_SMTP_PASSWORD="geheimesPasswort"

# Dauerhaft in ~/.bashrc oder ~/.profile
echo 'export SCANNER_SMTP_PASSWORD="geheimesPasswort"' >> ~/.bashrc

Für den Cron-Job die Variable in der Crontab oder in /etc/environment setzen:

SCANNER_SMTP_PASSWORD=geheimesPasswort
0 3 * * * cd /home/dschlueter/...

12. Konfigurationsreferenz

Alle Einstellungen in config.yaml. Fehlende Werte werden aus den Defaults übernommen.

Kern-Einstellungen

Schlüssel Standard Bedeutung
target Zu überwachende URL (Pflicht)
user_agent integrity-scanner/1.0 HTTP User-Agent
request_timeout 20 Timeout pro Seite in Sekunden

Crawl-Einstellungen (crawl:)

Schlüssel Standard Bedeutung
max_pages 200 Maximale Anzahl gecrawlter Seiten
delay_seconds 1.0 Pause zwischen Requests (Server-Schonung)
skip_extensions .jpg, .pdf etc. Dateitypen, die nicht gecrawlt werden

Score-Gewichte (scoring:)

Ereignis Standard-Punkte
Verdächtiger Dateiname (Webshell) 60
Neue externe Domain 50
Hidden Content (CSS-verstecktes Element mit Links/Text) 40
Meta-Refresh 40
Unerwartete Canonical-URL 35
Unerwartetes JSON-LD @type 35
Neue interne URL 30
Neues verdächtiges Inline-Script 30
Link in HTML-Kommentar 25
Großer Textblock (>200 Zeichen) 20
Fehlende interne URL 20
Neuer kaputte Link (4xx/5xx, nicht in Baseline) 20

Alle Werte sind in config.yaml unter scoring: anpassbar.

Alarm-Schwellen (thresholds:)

thresholds:
  yellow: 20                  # ab hier: YELLOW
  red: 60                     # ab hier: RED
  large_text_block_chars: 200 # ab dieser Zeichenanzahl gilt ein Textblock als "groß"

Erlaubte JSON-LD-Typen (allowed_jsonld_types:)

Alle @type-Werte, die in den strukturierten Daten der Website vorkommen dürfen. Unbekannte Typen lösen einen Fund aus (35 Punkte). Aktuell konfiguriert: Organization, WebSite, WebPage, Article, BreadcrumbList, LocalBusiness, Event, FAQPage u.a.

Security-Header (security_headers:)

Liste der Header, die auf jeder Seite erwartet werden. Fehlende Header werden im Report gemeldet, aber nicht bewertet (kein Score-Einfluss).


13. Externe Domains verwalten

Datei: config/allowed_external.yaml

# Legitime externe Domains — eine pro Zeile
- fonts.googleapis.com
- www.kloster-bredelar.de

Neue externe Domain erscheint im Scanner:

  1. Domain im Browser prüfen — gehört sie zur Website?
  2. Wenn legitim: in config/allowed_external.yaml eintragen
  3. python -m scanner approve --url <betroffene-Seite> oder --all

Wenn eine Domain nicht in der Liste steht, löst jedes Vorkommen 50 Punkte aus (YELLOW-Alarm, bei weiteren Funden RED).


14. Rausch-Unterdrückung konfigurieren

Manche Inhalte ändern sich automatisch (Datumsangaben, Zeitstempel) und würden sonst bei jedem Scan einen Fund auslösen. Zwei Mechanismen:

CSS-Selektoren ignorieren (ignore_selectors:)

Elemente werden vor dem Diff vollständig aus dem DOM entfernt:

normalization:
  ignore_selectors:
    - "#last-modified"
    - ".timestamp"
    - ".cookie-banner"

Datei: direkt in config.yaml oder in config/ignore_rules.yaml (kommentierte Beispiele dort vorhanden).

Text-Muster ignorieren (ignore_patterns:)

Reguläre Ausdrücke, die im normalisierten Text durch [IGNORED] ersetzt werden:

normalization:
  ignore_patterns:
    - '\b\d{1,2}\.\s*(?:Januar|Februar|März|April|Mai|Juni|Juli|August|September|Oktober|November|Dezember)\s+\d{4}\b'
    - '\b\d{1,2}\.\d{1,2}\.\d{4}\b'
    - 'Stand:\s+\S+'

15. Was der Scanner erkennt

Erkennt der Scanner ...

Prüfung Erkannt? Anmerkung
Neuen Text auf einer Seite Ja Ab 200 Zeichen bewertet
Versteckten Text (CSS display:none etc.) Ja 40 Punkte
Neue externe Domain Ja 50 Punkte
Meta-Refresh-Weiterleitung Ja 40 Punkte
Webshell-Dateien (shell.php, c99 etc.) Ja 60 Punkte
Links in HTML-Kommentaren Ja 25 Punkte
Geänderte Canonical-URL Ja 35 Punkte
Unbekanntes JSON-LD @type Ja 35 Punkte
eval(), atob(), XMLHttpRequest in Scripts Ja 30 Punkte
Kaputte interne Links (neu) Ja 20 Punkte
Kaputte interne Links (bekannt) Ja, informativ Kein Score
Fehlende Security-Header Ja, informativ Kein Score
Kaputte externe Links Nein Nur ob Domain neu ist
Veränderungen in Bilddateien Nein Nur Dateiname/URL
Serverseitige Code-Änderungen Nein Nur sichtbare HTML-Ausgabe

Grenzen des Scanners

  • Er prüft nur, was der Crawler als HTML-Seite sieht. Serverseitig eingeschleuster Code, der nur unter bestimmten Bedingungen aktiv wird (z.B. nur für Suchmaschinen-Bots), wird möglicherweise nicht erkannt.
  • Bilder und andere Binärdateien werden nicht auf Inhalt geprüft.
  • Externe Links werden nur auf Neuheit geprüft, nicht auf Erreichbarkeit.

16. Verzeichnisstruktur

config.yaml                          Hauptkonfiguration
config/
  allowed_external.yaml              Whitelist erlaubter externer Domains
  ignore_rules.yaml                  Kommentierte Beispiele für Rausch-Unterdrückung
data/
  baseline/                          Freigegebene Referenz (NIEMALS automatisch geändert)
    manifest.json                    Wer/was/wann freigegeben hat
    pages/                           Pro URL eine JSON-Datei (16-stelliger Hex-Name)
  snapshots/                         Ein Verzeichnis pro Crawl-Durchlauf
    YYYYMMDD_HHMMSS/
      manifest.json                  Zeitstempel, Seitenzahl, Fehler
      pages/                         Extrahierte Seiten-Daten
logs/
  scanner.log                        Detailliertes Laufzeit-Log
  cron.log                           Ausgabe der Cron-Ausführungen
reports/
  YYYYMMDD_HHMMSS/
    report.md                        Menschenlesbarer Report
    report.json                      Maschinenlesbarer Report (für spätere Auswertung)
scanner/                             Python-Paket (Quellcode)
tests/                               Automatisierte Tests

Tests ausführen

pytest tests/ -v                     # alle Tests
pytest tests/test_differ.py -v       # einzelnes Testmodul
pytest tests/test_differ.py::TestScoreDiff::test_green_for_no_changes -v   # einzelner Test