# 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. --- ## Für Einsteiger – in 3 Schritten Sie brauchen kein IT-Wissen. Das Programm zeigt eine **Ampel** und erklärt alles in einfachen Worten. **Schritt 1 — Erststand speichern** (einmalig, wenn die Website in Ordnung ist): ``` python -m scanner approve --all --note "Erststand geprüft" ``` **Schritt 2 — täglich prüfen** (am besten automatisch per Cron, siehe Abschnitt 10): ``` python -m scanner scan ``` **Schritt 3 — bei 🟡 oder 🔴 nachschauen:** ``` python -m scanner report ``` Was die Ampel bedeutet: | Ampel | Bedeutung | Was tun? | |---|---|---| | 🟢 | Alles in Ordnung | Nichts. | | 🟡 | Bitte nachschauen | Bericht lesen; wenn die Änderung gewollt war, mit `approve --all` bestätigen. | | 🔴 | Achtung | Website ansehen und die Person/Firma informieren, die Ihre Website betreut. | > **Wichtig:** Die selteneren Prüfungen (Tarnung, externe Links, Dateien) laufen > **automatisch einmal pro Woche** beim täglichen `scan` mit. Sie müssen dafür **nichts** > extra einrichten — ein einziger täglicher Aufruf genügt für alles. > > Beim ersten `approve --all` wird zusätzlich die **Datei-Überwachung** automatisch > eingerichtet (Bilder, Skripte, Stylesheets). Auch dafür ist kein weiterer Schritt nötig. Alles Weitere in dieser Anleitung richtet sich an **Fortgeschrittene** und an **Ihren Dienstleister**. --- ## Inhaltsverzeichnis 1. [Grundprinzip](#1-grundprinzip) 2. [Voraussetzungen und Installation](#2-voraussetzungen-und-installation) 3. [Erstinbetriebnahme](#3-erstinbetriebnahme) 4. [Täglicher Betrieb](#4-täglicher-betrieb) 5. [Befehle im Überblick](#5-befehle-im-überblick) 6. [Ergebnis-Level und Exit-Codes](#6-ergebnis-level-und-exit-codes) 7. [Berichte lesen](#7-berichte-lesen) 8. [Änderungen freigeben (approve)](#8-änderungen-freigeben-approve) 9. [Vorgehen bei einem Alarm](#9-vorgehen-bei-einem-alarm) 10. [Automatisierung per Cron-Job](#10-automatisierung-per-cron-job) 11. [E-Mail-Benachrichtigung einrichten](#11-e-mail-benachrichtigung-einrichten) 12. [Konfigurationsreferenz](#12-konfigurationsreferenz) 13. [Externe Domains verwalten](#13-externe-domains-verwalten) 14. [Rausch-Unterdrückung konfigurieren](#14-rausch-unterdrückung-konfigurieren) 15. [Was der Scanner erkennt](#15-was-der-scanner-erkennt) 16. [Verzeichnisstruktur](#16-verzeichnisstruktur) 17. [Cloaking-Erkennung](#17-cloaking-erkennung) 18. [Externe Links auf Erreichbarkeit prüfen](#18-externe-links-auf-erreichbarkeit-prüfen) 19. [Binärdateien und Assets prüfen](#19-binärdateien-und-assets-prüfen) --- ## 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 ```bash # 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 ```bash 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](#13-externe-domains-verwalten)). - Auffälligkeiten im Browser nachschauen. ### Schritt 3: Baseline freigeben Nur wenn alles sauber ist: ```bash 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 ```bash python -m scanner scan ``` Ergebnis erscheint direkt im Terminal. Der vollständige Report liegt in `reports/`. ### Automatischer Scan (Cron) ```bash crontab -e ``` Je eine Zeile pro überwachter Website einfügen (mit leichtem Zeitversatz): ```cron # bredelar.info — täglich 03:00 Uhr 0 3 * * * cd /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten && python -m scanner --config bredelar.info/config.yaml scan >> bredelar.info/logs/cron.log 2>&1 # jamulix.de — täglich 03:15 Uhr 15 3 * * * cd /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten && python -m scanner --config jamulix.de/config.yaml scan >> jamulix.de/logs/cron.log 2>&1 ``` Danach prüfen: `crontab -l` 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 ` 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 | | `cloak-check` | Doppel-Crawl: Browser-UA vs. Googlebot (Cloaking-Erkennung) | | `check-ext-links` | Externe Links auf Erreichbarkeit prüfen (4xx/5xx) | | `check-assets` | Binärdateien (JS/CSS/Bilder) auf Inhaltsänderung prüfen | | `approve-assets` | Aktuelle Asset-Hashes als neue Baseline setzen | ### Beispiele ```bash # 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** | 0–19 | `0` | Keine Auffälligkeiten | | **YELLOW** | 20–59 | `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`. ```bash # 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: ```bash python -m scanner approve --url https://bredelar.info/veranstaltungen/ \ --note "Veranstaltungskalender aktualisiert" ``` ### Alle Änderungen freigeben Nach Prüfung aller markierten URLs: ```bash 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: ```bash 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 --note "Grund"` - Nein: Abschnitt [Angriff](#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: ```bash 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-Einträge einrichten ```bash crontab -e ``` Je eine Zeile pro überwachter Website einfügen — mit **Zeitversatz von mindestens 15 Minuten** zwischen den einzelnen Scans. Ein Scan lädt alle Seiten einer Website herunter; laufen mehrere Scans gleichzeitig, konkurrieren sie um Netzwerk und CPU und können sich gegenseitig verlangsamen oder die überwachten Server unnötig belasten. ```cron # bredelar.info — täglich 03:00 Uhr 0 3 * * * cd /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten && python -m scanner --config bredelar.info/config.yaml scan >> bredelar.info/logs/cron.log 2>&1 # jamulix.de — täglich 03:15 Uhr 15 3 * * * cd /home/dschlueter/Python_Programs/integrity_scanner_fuer_statische_Webseiten && python -m scanner --config jamulix.de/config.yaml scan >> jamulix.de/logs/cron.log 2>&1 ``` Danach prüfen ob die Einträge gespeichert wurden: ```bash crontab -l ``` **Neue Website hinzufügen:** 1. Template kopieren: `cp -r meine-seite.de neue-seite.de` 2. In `neue-seite.de/config.yaml` `target` und alle 4 Pfade (`data_dir`, `reports_dir`, `logs_dir`, `config_dir`) anpassen 3. Erste Prüfung: `python -m scanner --config neue-seite.de/config.yaml init` 4. Weiteren Cron-Eintrag ergänzen — mit 15 Minuten Abstand zum vorherigen (z.B. 03:30 Uhr) 5. `neue-seite.de/data/`, `/reports/`, `/logs/` in `.gitignore` eintragen ### Log prüfen ```bash tail -50 bredelar.info/logs/cron.log tail -50 jamulix.de/logs/cron.log ``` ### 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 ```yaml 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: ```bash # 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: ```cron 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 | | Cloaking: Link nur im Bot-Crawl sichtbar | 60 | | Neue externe Domain | 50 | | Hidden Content (CSS-verstecktes Element mit Links/Text) | 40 | | Meta-Refresh | 40 | | Cloaking: signifikant mehr Text im Bot-Crawl (>100 Zeichen) | 40 | | Geänderte JavaScript-Datei (`check-assets`) | 40 | | Unerwartete Canonical-URL | 35 | | Unerwartetes JSON-LD `@type` | 35 | | Geänderte CSS-Datei (`check-assets`) | 30 | | Neue interne URL | 30 | | Neues verdächtiges Inline-Script | 30 | | Link in HTML-Kommentar | 25 | | Cloaking: `Vary: User-Agent` Header (ohne Inhaltsdiff) | 25 | | Großer Textblock (>200 Zeichen) | 20 | | Fehlende interne URL | 20 | | Neuer kaputte Link (4xx/5xx, nicht in Baseline) | 20 | | Geänderte Bild-/Binärdatei (`check-assets`) | 20 | Alle Werte sind in `config.yaml` unter `scoring:` anpassbar. ### Alarm-Schwellen (`thresholds:`) ```yaml 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` ```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 ` 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: ```yaml 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: ```yaml 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 (4xx/5xx) | Ja, mit `check-ext-links` | Kein Score, nur Report | | UA-basiertes Cloaking (Googlebot sieht anderen Inhalt) | Ja, mit `cloak-check` | Score 40–60 | | Veränderungen in JS/CSS/Bildern (Inhalt) | Ja, mit `check-assets` | Score 20–40 | | IP-basiertes Cloaking | Nein | Nur Google Search Console kann das erkennen | | Serverseitige Code-Änderungen | Nein | Nur sichtbare HTML-Ausgabe | ### Grenzen des Scanners - **IP-basiertes Cloaking** wird nicht erkannt: Wenn der Server je nach Herkunfts-IP unterschiedliche Inhalte liefert (nur für echte Googlebot-IPs), sieht der Scanner immer den normalen Inhalt. Ergänzung: Google Search Console → URL-Prüfung. - Serverseitig injizierter Code, der nur unter bestimmten Bedingungen aktiv wird, ist nur per `cloak-check` (UA-Variante) teilweise erkennbar. --- ## 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) asset_hashes.json SHA-256-Hashes aller freigegebenen Assets (approve-assets) 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/ Normaler Scan-Report report.md report.json YYYYMMDD_HHMMSS_cloak/ Cloaking-Check-Report report.md report.json YYYYMMDD_HHMMSS_ext-links/ Externer-Links-Report report.md report.json YYYYMMDD_HHMMSS_assets/ Asset-Check-Report report.md report.json scanner/ Python-Paket (Quellcode) tests/ Automatisierte Tests ``` ### Tests ausführen ```bash 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 ``` --- ## 17. Cloaking-Erkennung Cloaking bedeutet: der Server liefert Suchmaschinen-Bots absichtlich anderen Inhalt als normalen Besuchern — ein klassisches Parasite-SEO-Angriffsmuster. ### Befehl ```bash python -m scanner cloak-check ``` Der Scanner crawlt die Website zweimal nacheinander: einmal mit dem normalen Browser-User-Agent, einmal als `Googlebot/2.1`. Unterschiede im Text, in den Links oder im `Vary`-Header werden gemeldet. ### Scoring | Fund | Punkte | |---|---| | Link nur im Bot-Crawl sichtbar | 60 (RED) | | Bot sieht >100 Zeichen mehr Text | 40 | | `Vary: User-Agent` Header ohne Inhaltsdiff | 25 | ### Report Der Report liegt unter `reports/_cloak/report.md` und zeigt pro auffälliger Seite: - ob `Vary: User-Agent` gesetzt ist - welche Links nur der Bot sieht - wie viele Zeichen mehr Text der Bot bekommt - einen Text-Diff (bis zu 20 Zeilen) ### Grenzen Erkennt nur **UA-basiertes** Cloaking. IP-basiertes Cloaking (Server prüft die Herkunfts-IP des Crawlers) ist damit nicht erkennbar — dafür ist die Google Search Console das richtige Werkzeug (URL-Prüfung → Live testen). ### Wann ausführen? **Läuft automatisch wöchentlich** beim täglichen `scan` mit (steuerbar über `periodic_checks` in der `config.yaml`). Der manuelle Aufruf ist nur bei konkretem Verdacht nötig. --- ## 18. Externe Links auf Erreichbarkeit prüfen Der tägliche `scan` erkennt *neue* externe Domains, prüft aber nicht ob bestehende externe Links noch funktionieren. Dafür gibt es: ```bash python -m scanner check-ext-links ``` ### Was geprüft wird Alle externen ``-Links aus dem letzten Snapshot werden per HEAD-Request abgefragt (Fallback auf GET bei HTTP 405). Gemeldet werden: - **Kaputte Links** (HTTP 4xx/5xx) — mit Angabe, auf welchen Seiten sie verlinkt sind - **Verbindungsfehler** (Timeout, DNS-Fehler) — ebenfalls mit Quelladressen ### Scoring Kaputte externe Links werden **nicht bewertet** (kein Score-Einfluss). Das Ergebnis ist rein informativ — externe Links brechen regelmäßig ohne Angriffsbezug. Exit-Code: `0` wenn alle Links erreichbar, `1` wenn mindestens ein kaputtes oder fehlendes Link gefunden wurde. ### Report Der Report liegt unter `reports/_ext-links/report.md`. ### Wann ausführen? **Läuft automatisch wöchentlich** beim täglichen `scan` mit (steuerbar über `periodic_checks` in der `config.yaml`). Ein manueller Aufruf ist nur bei Bedarf nötig. --- ## 19. Binärdateien und Assets prüfen Der tägliche `scan` erkennt Änderungen im HTML-Text und an Link-URLs, aber nicht ob der *Inhalt* einer Bilddatei oder eines JavaScript-Files ausgetauscht wurde. Dafür: ### Ersteinrichtung (einmalig) ```bash python -m scanner crawl # aktuellen Snapshot anlegen (falls noch keiner vorhanden) python -m scanner approve-assets # SHA-256-Hashes aller Assets als Baseline speichern ``` ### Regelmäßige Prüfung ```bash python -m scanner check-assets ``` Alle verlinkten JS-, CSS- und Bilddateien aus dem letzten Snapshot werden heruntergeladen und per SHA-256 gehasht. Abweichungen von der gespeicherten Baseline werden gemeldet. ### Scoring | Dateityp | Punkte | |---|---| | JavaScript (`.js`) | 40 — YELLOW | | CSS (`.css`, Stylesheets) | 30 — YELLOW | | Bilder, PDFs, sonstige | 20 — YELLOW | | 2× JS geändert | 80 — RED | ### Änderungen freigeben Nach einem legitimen Update (neues CDN-Bundle, neues Logo): ```bash python -m scanner approve-assets --note "CDN-Update nach Redesign Juli 2026" ``` `approve-assets` fetcht alle Assets frisch und setzt die neuen Hashes als Baseline. ### Report Der Report liegt unter `reports/_assets/report.md` und zeigt pro geänderter Datei: - den Dateityp - die ersten 16 Zeichen des alten und neuen SHA-256-Hash - die Größenänderung in Bytes ### Wann ausführen? **Läuft automatisch wöchentlich** beim täglichen `scan` mit (steuerbar über `periodic_checks` in der `config.yaml`). Die Datei-Vergleichsgrundlage wird zudem beim ersten `approve --all` automatisch angelegt — `approve-assets` ist nur für den manuellen Sonderfall gedacht.