IT security, FreeBSD, Linux, mail server hardening, post-quantum crypto, DNS, retro computing & hands-on hardware hacks. Privater Tech-Blog seit 2003.

Schlagwort: InfoSec (Seite 2 von 15)

IP-Kameras: Risiken, Portfreigaben (RTSP/HTTP) & Checks

Moin, ich mag noch einmal etwas zu IP-Überwachungskameras schreiben. Ihr erinnert euch vielleicht an meinen letzten Beitrag zu diesem Thema KLICK.

article image of ip cameras

Dort habe ich mich speziell auf den RTSP-Port bezogen und auch https://www.shodan.io/ als Beispiel genannt. Shodan scannt unaufhörlich IPv4-Adressen (bei IPv6 wäre ein flächendeckender Scan kaum praktikabel) und stellt seine Ergebnisse öffentlich zur Verfügung. Sicherheitsforscher — aber leider auch Menschen mit schlechten Absichten — bedienen sich solcher Dienste, um Informationen über Systeme hinter einer IPv4-Adresse zu bekommen, ganz ohne selbst groß zu scannen oder zu testen. Grob gesagt: Google für „Hacker“.

Dass IP-Kameras — vor allem günstige oder ältere Modelle — schnell ein Sicherheitsrisiko darstellen, habe ich schon erwähnt; wer das hier liest, weiß es in der Regel auch. Automatische Portfreigaben in Kombination mit solchen Kameras sind oft problematisch. Wenn ich über so etwas stolpere und ohne großen Aufwand den Betreiber ausfindig machen kann, versuche ich jeweils per E-Mail oder einem kurzen Anruf zu warnen. Das stößt mal auf offene Ohren, manchmal wird es komplett ignoriert (manchmal mit Abschalten des öffentlichen Zugriffs, manchmal ohne); selten kommt die Antwort „Anzeige ist raus“.

Kameras filmen oft sensible Bereiche, sowohl innen als auch außen. Das kann viele Probleme mit sich bringen, wenn diese Informationen einfach öffentlich zugänglich sind. Anders als bei Datei-Freigaben scheint bei Kamerastreams noch nicht die nötige Awareness vorhanden zu sein — genau deshalb informiere ich hier und weise darauf hin.

Es ist nicht nur der RTSP-Stream, der sich häufig per UPnP seinen Weg nach draußen „tunnelt“. Oft werden auch per DNAT / Portfreigabe / Portweiterleitung die Webinterfaces der Kameras direkt aus dem Internet erreichbar gemacht. Im schlimmsten Fall kann man also mit dem Browser direkt auf Webinterface und Stream zugreifen. Viele sichern den Zugriff mit der kameraeigenen Anmeldung — das ist schon mal ein Anfang. Leider reicht das nicht immer: Bei manchen Modellen sind Funktionen wie Snapshots oder einzelne JPEG-Endpoints weiterhin ohne Anmeldung erreichbar. Das ist auf den ersten Blick nicht sichtbar — kennt man aber die entsprechende URL, genügt ein Browseraufruf und man sieht wieder alles.

Deshalb gebe ich immer den Rat: Zugriff lieber hinter ein VPN legen und niemals direkt offen ins Internet. Gebt jedem Gerät und jedem Dienst, den ihr aus dem Internet erreichbar macht, mindestens so viel Vertrauen wie eurer Haustür. Und prüft regelmäßig, ob dieses Vertrauen noch gerechtfertigt ist.

Wer selbst prüfen möchte, ob die EIGENE Kamera trotz eingerichteter Anmeldung noch irgendwie ohne Login zugänglich ist, kann mein kurzes Python-Tool nutzen: https://github.com/Kernel-Error/cam_probe

Denkt also bitte einmal darüber nach, ob ihr allem, was ihr direkt mit dem Internet verbunden habt, mindestens das gleiche Vertrauen entgegenbringt wie eurer Haustür oder Wohnungstür. Denkt an die security.txt und daran, dass, wenn sich jemand die Mühe macht, euch über ein solches Problem zu informieren, diese Person damit wahrscheinlich den größten Aufwand und auch das größte Risiko für sich selbst aufnimmt – nur, um euch auf ein Problem hinzuweisen.
Einen solchen Fund zu ignorieren, zu verkaufen oder sonst wie auszunutzen, ist deutlich einfacher, als den Betreiber zu informieren.

Natürlich gibt es auch hier schwarze Schafe, aber die Vertrauenswürdigkeit einer solchen Nachricht lässt sich meist schnell per Google oder auch ChatGPT prüfen.
Frage? Dann fragen. 🙂

GPT in Rspamd aktivieren: so nutze ich das LLM-Signal im Score

Rspamd web interface showing GPT module spam scores

Seit einiger Zeit nutze ich das GPT-Modul von Rspamd, um bei der Spam-Erkennung ein zusätzliches Signal zu bekommen. Es ersetzt nichts — kein Bayes, kein DKIM, kein RBL — sondern ist ein weiterer Sensor im Gesamtbild. Wer sich fragt, wie das in der Praxis aussieht und worauf man achten muss: hier mein aktuelles Setup.

Update 2026-02-13: Dieser Beitrag wurde komplett überarbeitet. Die ursprüngliche Version nutzte json=false, was zu Parse-Problemen führte. Außerdem fehlte ein Custom Prompt — und genau das ist der entscheidende Punkt, wie sich herausgestellt hat.

Voraussetzungen

  • Rspamd >= 3.12 mit GPT-Plugin (bei mir aktuell 3.14.0 auf FreeBSD 15.0)
  • Ein OpenAI API-Key (oder kompatibler Endpoint)
  • Grundverständnis von Rspamd Metrics und Actions

OpenAI API-Key anlegen

OpenAI API usage dashboard for Rspamd GPT integration

Wer noch keinen Key hat: Auf platform.openai.com einloggen, unter API Keys einen neuen Service-Account-Key erzeugen. Der Key wird nur einmal angezeigt — sicher ablegen. Den Verbrauch sieht man im Dashboard. Bei gpt-4o-mini und Mailfiltering sind die Kosten minimal.

Die Konfiguration: gpt.conf

Hier meine aktuelle /usr/local/etc/rspamd/local.d/gpt.conf:

enabled = true;
type = "openai";
model = "gpt-4o-mini";
api_key = "GEHEIMER-KEY";

model_parameters {
  gpt-4o-mini {
    max_tokens = 160;
    temperature = 0.0;
  }
}

timeout = 10s;
allow_ham = true;
allow_passthrough = false;
json = true;

prompt = "You are an email spam detector. Analyze the email and respond with ONLY a JSON object, no other text. The JSON must have these fields: "probability" (number 0.00-1.00 where 1.0=spam, 0.0=ham), "reason" (one sentence citing the strongest indicator). Example: {"probability": 0.85, "reason": "Unsolicited offer with urgent language and suspicious links."}  LEGITIMATE patterns: verification emails with codes, transactional emails (receipts, confirmations), newsletter unsubscribe links. Flag as spam only with MULTIPLE red flags: urgent threats, domain impersonation, requests for credentials, mismatched URLs.";

symbols_to_except {
  RCVD_IN_DNSWL_MED   = -0.1;
  RCVD_IN_DNSWL_HI    = -0.1;
  DWL_DNSWL_MED        = -0.1;
  WHITELIST_RECP_ADDR = -0.1;
  BAYES_HAM           = -0.1;
  SPAMTRAP            = 0;
  RCPT_IN_SPAMTRAP    = 0;
  SPAMTRAP_ADDR       = 0;
  RCVD_VIA_SMTP_AUTH  = 0;
  LOCAL_CLIENT        = 0;
  FROM_LOCAL          = 0;
}

Was hat sich gegenüber der alten Version geändert?

json = true und der Custom Prompt

Das ist die wichtigste Änderung. In meiner ursprünglichen Konfiguration stand json = false. Das funktionierte, hatte aber einen Haken: die Antwort des Modells wurde als Freitext geparst, was unzuverlässig war.

Mit json = true aktiviert Rspamd den JSON-Modus. Das Modell wird angewiesen, strukturiertes JSON zurückzuliefern, und der Parser erwartet ein Feld probability in der Antwort.

Und hier kommt der Fallstrick: Der Default-Prompt von Rspamd passt nicht zum JSON-Modus. Er fordert das Modell auf, nummerierte Textzeilen zurückzugeben:

Output ONLY 2 lines:
1. Numeric score: 0.00-1.00
2. One-sentence reason...

Der JSON-Parser erwartet aber:

{"probability": 0.85, "reason": "..."}

Das Ergebnis: cannot convert spam score im Log und GPT_UNCERTAIN(0.00) bei jeder Mail. Das GPT-Modul lief, lieferte aber nie ein verwertbares Ergebnis.

Lösung: ein Custom Prompt, der explizit JSON mit dem probability-Feld verlangt. Damit funktioniert die Kette:

  1. Rspamd sendet Mail + Prompt an OpenAI
  2. OpenAI antwortet mit {"probability": 0.9, "reason": "..."}
  3. Rspamd parst das JSON, findet probability, mappt auf GPT_SPAM/GPT_HAM/GPT_SUSPICIOUS

reason_header entfernt

In der alten Version hatte ich reason_header = "X-GPT-Reason" gesetzt. Das schrieb die GPT-Begründung als eigenen Header in die Mail. Mit json = true ist das nicht mehr nötig — die Reason steckt im JSON und taucht im Rspamd-Log auf. Außerdem entferne ich ohnehin GPT-Header per Milter-Config, damit keine internen Analyse-Details an den Empfänger durchsickern.

symbols_to_except angepasst

Änderungen gegenüber der alten Version:

  • GREYLIST entfernt: Greylisting ist kein Vertrauens-Signal. Eine Mail die Greylisting besteht, kann trotzdem Spam sein. GPT soll diese Mails weiterhin bewerten.
  • BAYES_HAM hinzugefügt: Wenn Bayes die Mail bereits sicher als Ham einstuft, spart man sich den GPT-Call. Sinnvoll für Newsletter und regelmäßige Korrespondenz.
  • SPAMTRAP-Symbole hinzugefügt: Mails an Spamtrap-Adressen brauchen keine GPT-Analyse, die sind per Definition Spam.

Scoring: Gewichte und Thresholds

Die GPT-Symbole und ihre Gewichte in der metrics.conf (bzw. local.d/groups.conf):

symbols {
  GPT_SPAM       { weight = 9.0;  description = "GPT: classified as SPAM"; }
  GPT_SUSPICIOUS { weight = 4.5;  description = "GPT: classified as SUSPICIOUS"; }
  GPT_HAM        { weight = -0.5; one_shot = true; description = "GPT: classified as HAM"; }
}

Warum diese Gewichte?

  • GPT_SPAM (9.0): Kräftig, aber alleine nicht genug zum Rejecten. Erst in Kombination mit anderen Signalen (Bayes, RBL, fehlende Auth) wird der Reject-Threshold erreicht.
  • GPT_SUSPICIOUS (4.5): Schiebt Grenzfälle in Richtung Greylist oder Add-Header. Genau dafür ist GPT am nützlichsten.
  • GPT_HAM (-0.5): Bewusst niedrig und one_shot. GPT soll Spam erkennen, nicht Ham retten.

Dazu die Action-Thresholds:

actions {
  greylist   = 4;
  add_header = 6;
  reject     = 12;
}

Reject-Threshold bei mir: 12 statt Default 15. Das geht, weil die traditionellen Checks (SPF, DKIM, DMARC, RBL, Bayes, DNSBL) bereits solide arbeiten. GPT kommt als zusätzliches Signal obendrauf.

Praxis-Beispiel

Hier eine echte Spam-Mail aus dem Log, bei der GPT korrekt angeschlagen hat:

rspamd_task_write_log: (default: T (reject): [13.83/12.00]
  [BAYES_SPAM(5.10){100.00%;},
   ABUSE_SURBL(5.00){next.schnapper-empfehlung.de:url;...},
   GPT_SPAM(2.40){0.9;},
   FROM_NEQ_ENVFROM(0.50){...},
   FORGED_SENDER(0.30){...},
   ...]

Was man hier sieht:

  • GPT_SPAM(2.40){0.9;} — GPT hat Probability 0.9 (90% Spam) zurückgeliefert. Rspamd mappt den Probability-Wert nicht 1:1 auf das konfigurierte Gewicht, sondern skaliert intern — hier ergeben sich 2.40 von maximal 9.0 Punkten.
  • Zusammen mit BAYES_SPAM (5.10) und ABUSE_SURBL (5.00) kommt die Mail auf 13.83 — deutlich über dem Reject-Threshold von 12.
  • GPT war hier nicht das ausschlaggebende Signal, hat aber zur Gesamtbewertung beigetragen.

Das ist genau das Verhalten, das ich will: GPT als ein Baustein unter vielen, der bei Grenzfällen den Ausschlag geben kann.

Datenschutz

Das muss gesagt werden: Mit diesem Setup fließen Mailinhalte an OpenAI. Wer personenbezogene Daten verarbeitet oder in einem regulierten Umfeld arbeitet, muss prüfen ob das zulässig ist. Alternative: selbst gehostete Modelle über Ollama oder kompatible lokale Endpoints. Rspamd unterstützt das über den type-Parameter.

Für meinen privaten Mailserver ist das Risiko vertretbar — und die Ergebnisse sprechen für sich.

Update 2026-05-06: Rspamd hat den Default-Prompt deutlich verbessert

Im Feedback zu diesem Beitrag kam ein guter Hinweis aus dem Fediverse von @bash2@momou.social: Vsevolod Stakhov, Maintainer von Rspamd, hat am 2. Oktober 2025 den Default-Prompt komplett überarbeitet. Commit 893ee871, Titel „Improve LLM prompt and add sender frequency tracking“. Der neue Prompt ist deutlich strukturierter, mit expliziten Sektionen für legitime Muster (Verifikations-Mails, Transaktions-Mails, Password-Resets) und für Phishing-Indikatoren, die mehrfach auftreten müssen, bevor klassifiziert wird. Das senkt False-Positives auf legitime Absender und ist eine klare Verbesserung gegenüber dem alten, knappen Default. Danke für den Hinweis!

Wichtig zur Einordnung: Der neue Default-Prompt liefert weiterhin strukturierten Plain-Text, kein JSON. Output-Format sind 2 bis 3 fest definierte Zeilen (Score, Reason, optional Kategorie). Was das für die beiden gängigen Setups bedeutet:

  • json = false: Wer ohne JSON-Modus fährt, profitiert direkt vom neuen Default-Prompt. Rspamd auf eine Version mit dem Commit aktualisieren, fertig.
  • json = true wie in diesem Beitrag: Custom Prompt mit JSON-Format bleibt Pflicht. Der neue Default ist immer noch kein JSON und kollidiert weiterhin mit dem Parser.

Zusätzlich neu im selben Commit: Sender-Frequency-Tracking. Rspamd klassifiziert in lualib/llm_context.lua jeden Absender per Redis-Counter als new, occasional, known oder frequent und gibt dem Modell die Info als Context-Snippet mit. Der Prompt weist das LLM dann an, bei known oder frequent die Phishing-Wahrscheinlichkeit zu reduzieren, sofern keine starken Gegen-Signale vorliegen. Voraussetzung ist, dass die LLM-Context-Funktion mit Redis-Backend läuft, weil das Feature über den sender_counts-Counter dort getrackt wird.

Mein Setup hier im Beitrag bleibt also unverändert: json = true plus Custom Prompt, der explizit ein probability-Feld verlangt. Wer stattdessen den neuen Default-Prompt nutzen möchte, sollte gleichzeitig auf json = false umstellen, sonst läuft man wieder in die alte Falle aus diesem Beitrag.

Zusammenfassung

ParameterWertWarum
jsontrueStrukturiertes Parsing, zuverlässiger als Freitext
promptCustomPflicht bei json=true! Default-Prompt liefert Textformat, Parser erwartet JSON
temperature0.0Deterministische Antworten, kein Kreativitäts-Bonus beim Spamfiltern
allow_hamtrueKleines positives Signal für legitime Mails
symbols_to_exceptBAYES_HAM, DNSWL, Whitelists, SMTP_AUTH, SpamtrapsUnnötige API-Calls vermeiden
reason_headernicht gesetztNicht nötig mit json=true, interne Details gehören nicht in den Header

Die wichtigste Erkenntnis: json = true ohne Custom Prompt ist kaputt. Der Default-Prompt und der JSON-Parser sprechen unterschiedliche Sprachen. Wer json = true setzt, muss einen Prompt mitliefern, der JSON mit einem probability-Feld verlangt. Sonst steht im Log cannot convert spam score und GPT liefert nur GPT_UNCERTAIN(0.00).

Siehe auch: rspamd mit Dovecot und IMAPSieve

Fragen? Einfach melden.

Volksverschlüsselung wird eingestellt

An mir flog gerade die Information vorbei, dass die Volksverschlüsselung zum 31.01.2026 eingestellt wird. Ich habe mir dort vor ein paar Jahren mal ein S/MIME-Zertifikat für meine E-Mails geholt. Mir hat der Ansatz gefallen, dass man sich mit seinem neuen Personalausweis und dessen Online-Funktion dort legitimieren kann und im Anschluss sein Zertifikat bekommt. Für mich hat diese Abhängigkeit absolut Sinn ergeben. Wir haben ja schon alle einen Perso mit Online-Funktion und PIN und was weiß ich alles. Also warum nicht auch einfach und schnell Zertifikate darüber erstellen?

Screenshot der Meldung zur Einstellung des Dienstes Volksverschlüsselung.

Funktioniert hat das alles wirklich gut – leider mit dem gleichen Problemchen wie auch bei cacert.org: Die Root-Zertifikate sind nicht in den Trust Stores der Betriebssysteme, Browser, Mailclients usw. Signiert man also etwas damit, wird es beim Empfänger als ungültig und unsicher angezeigt, es sei denn, dieser installiert manuell die Root-Zertifikate. Das macht natürlich niemand. Damit war die Volksverschlüsselung für mich genauso raus wie leider auch cacert.org.

Nun hing hinter der Volksverschlüsselung ebenfalls das Fraunhofer-Institut. Meine Hoffnung war, dass über diesen Weg am Ende doch mal die Root-Zertifikate in die Trust Stores kommen. Aber leider nicht.

Die Diskussion, ob man Trust Stores wirklich braucht, ob man sie vor allem vorgefüllt braucht, mache ich genauso wenig auf wie DNSSEC und TLSA, ok? Denn uns allen ist ja inzwischen klar, dass wir es „sicher“ haben könnten – wenn man nur nicht so scheiß viel Geld mit den CAs verdienen könnte. Denn die ganzen CAs zahlen ja schon ein paar Euro, um in die Trust Stores zu kommen.

Soviel dann also zur Volksverschlüsselung.

Siehe auch: S/MIME per DNS mit SMIMEA

Fragen? Einfach melden.

Sicherheitslücken melden: Mein Umgang mit einem Vulnerability Report

Vulnerability Report

Vor Kurzem habe ich einen Vulnerability Report erhalten. Ich freue mich über solche Hinweise. Sie helfen mir, mein Setup zu verbessern, bevor jemand eine Schwachstelle tatsächlich ausnutzt.

Der Report

Subject: Vulnerability Report: Vulnerable System Detected at openpgpkey.kernel-error.com

Hello Team,

I have identified a security issue in your system related to a vulnerability
(CVE-2023-48795) in Terrapin.

Vulnerability Details:
- CVE Identifier: CVE-2023-48795
- Vulnerability Type: javascript
- Severity: medium
- Host: openpgpkey.kernel-error.com
- Affected Port: 22

[...]

Best Regards,
Security Team

Erste Einschätzung

Terrapin hatte ich eigentlich schon überall gepatcht. Dann der Hinweis auf openpgpkey.kernel-error.com. Die Domain existiert als CNAME und gehört zur Web Key Directory (WKD), damit GPG-Keys automatisiert abgerufen werden können. Ich habe das als CNAME zu wkd.keys.openpgp.org angelegt, weil dieser Keyserver eine E-Mail-Validierung beim Hochladen durchführt.

Der betroffene SSH-Server gehört also gar nicht zu meiner Infrastruktur. Ich kann selbst nichts tun.

Vulnerability Type: JavaScript bei einem SSH-Problem auf Port 22? Der Finder hat vermutlich sein Standard-Template benutzt und nicht angepasst. Aber ich wollte trotzdem prüfen, ob seine Einschätzung zum SSH-Server zutrifft:

# ssh-audit openpgpkey.kernel-error.com (gekürzt)
(gen) banner: SSH-2.0-OpenSSH_8.4p1 Debian-5+deb11u3
(gen) software: OpenSSH 8.4p1

(cve) CVE-2021-41617  -- (CVSSv2: 7.0) privilege escalation via supplemental groups
(cve) CVE-2016-20012  -- (CVSSv2: 5.3) enumerate usernames via challenge response

(kex) ecdh-sha2-nistp256  -- [fail] suspected NSA backdoor
(kex) ecdh-sha2-nistp384  -- [fail] suspected NSA backdoor
(kex) kex-strict-s-v00@openssh.com  -- [info] Terrapin counter-measure present

(key) ssh-rsa (2048-bit)  -- [fail] broken SHA-1 hash algorithm
(key) ecdsa-sha2-nistp256 -- [fail] suspected NSA backdoor

(mac) hmac-sha1-etm@openssh.com  -- [fail] broken SHA-1 hash algorithm
(mac) hmac-sha1                  -- [fail] broken SHA-1

Sieht tatsächlich nicht optimal aus. NIST-Kurven, SHA-1, 2048-Bit RSA. Der Hinweis war also nicht unberechtigt. Ich habe dem Finder freundlich und dankbar geantwortet, aber darauf hingewiesen, dass das System nicht zu meiner Infrastruktur gehört. Die relevanten WHOIS-Informationen zur IP habe ich mitgeschickt.

Die Antwort

Thank you for your answer.

Let me know if you need anything else from myside

I hope this type of hard efforts deserves something reward

„Hard efforts“. Ich will das nicht schlechtreden. Im beruflichen Umfeld hätte ich mich vielleicht sogar für eine Kleinigkeit stark gemacht. Aber hier geht es um meine private Infrastruktur, und dann noch mit dem JavaScript-Hinweis und der Meldung zu einem fremden System. Das wirkt oberflächlich. Also habe ich ihn freundlich darauf hingewiesen.

Wie man mit Vulnerability Reports umgehen sollte

Wenn euch eine solche Nachricht erreicht: Schnell und freundlich reagieren. Den Report ernst nehmen, bewerten und eine angemessene Rückmeldung geben. Die Mühe des Finders wertschätzen. Zwei Wochen später mit „Anzeige ist raus!“ zu antworten wäre der falsche Weg. Es ist für jemanden deutlich aufwendiger, eine Meldung zu schreiben, als das Ganze in ein Darknet-Forum zu posten und dort ein paar XMR einzusammeln.

Macht es den Leuten einfach, euch zu kontaktieren. Eine security.txt oder klare Kontaktinformationen für eine Security-Mailbox helfen ungemein. Hauptsache, jemand kann seinen Report unkompliziert abgeben, und er wird von jemandem gelesen, der das bewerten kann.

Mehr zum Thema security.txt:
securitytxt.org | Wikipedia | BSI Allianz für Cybersicherheit

Zum Vergleich: Mein eigener SSH-Server

So sieht meine SSH-Konfiguration von außen aus:

# ssh-audit bsd01.kernel-error.de (gekürzt)
(gen) banner: SSH-2.0-OpenSSH_9.7 DemMeisterSeinRennAuto
(gen) software: OpenSSH 9.7

(kex) sntrup761x25519-sha512@openssh.com  -- [info] Post-Quantum Key Exchange
(kex) curve25519-sha256                   -- [info] default since OpenSSH 6.4
(kex) diffie-hellman-group16-sha512       -- [info] available since OpenSSH 7.3
(kex) kex-strict-s-v00@openssh.com        -- [info] Terrapin counter-measure

(key) ssh-ed25519                         -- [info] available since OpenSSH 6.5

(enc) aes256-gcm@openssh.com              -- [info] available since OpenSSH 6.2
(enc) aes128-gcm@openssh.com              -- [info] available since OpenSSH 6.2

(mac) hmac-sha2-256-etm@openssh.com       -- [info] available since OpenSSH 6.2
(mac) hmac-sha2-512-etm@openssh.com       -- [info] available since OpenSSH 6.2

Keine NIST-Kurven, kein SHA-1, kein RSA, Post-Quantum Key Exchange mit sntrup761. Nur Ed25519 als Host Key. Der Banner ist übrigens Absicht.

Fragen? Einfach melden.

IP-Kameras als Sicherheitsrisiko: GeoGuessr und Datenschutz im Fokus​

Die meisten von euch werden das Spiel GeoGuessr kennen. Man bekommt ein Google-Street-View-Bild gezeigt, kann sich vielleicht noch ein paar Meter hin- und herbewegen und muss dann auf einer Weltkarte einen Marker setzen, wo man meint, dass dieses Bild aufgenommen wurde. Wer dem Punkt am nächsten kommt, gewinnt.

Eine etwas abgewandelte Version begegnet mir immer mal wieder, wenn ich mich an einem Bug-Bounty-Programm beteilige oder einfach mit offenen Augen durchs Internet spaziere. Damit ist natürlich kein aktives Port-Scanning auf Netzblöcke oder Ähnliches gemeint.

Worum geht es genau?

In den letzten Jahren verbreiten sich immer mehr billige „China-Kameras“ bei Heimanwendern, aber auch bei Unternehmen. Dagegen spricht erst einmal nichts.

Was leider oft übersehen wird, sind die kleinen automatischen Helferlein, die die Einrichtung und den Betrieb einer solchen Kamera möglichst einfach machen sollen. UPnP (Universal Plug and Play) haben manche vielleicht schon mal im Zusammenhang mit Windows 95 oder USB gehört (ja, ich bin alt …). So etwas gibt es aber auch für Router und Firewalls, also für Netzwerke. Bei einer Fritzbox nennt sich das beispielsweise „Automatische Portfreigabe“.

Der eine oder andere ahnt jetzt sicher schon etwas: Es gibt IP-Kameras, die sich so – vielleicht sogar ohne das Wissen des Betreibers – selbst über das Internet erreichbar machen. Das betrifft nicht selten die komplette Weboberfläche. Mal ist diese kennwortgeschützt, mal nicht.

Sehr oft findet sich auch nur der RTSP-Port (Real Time Streaming Protocol) offen im Internet. Per RTSP werfen solche Kameras oft einen einfachen Videostream aus, der die Anbindung an zentrale Videoüberwachungssysteme erlaubt. Auch RTSP-Streams lassen sich mit einer Anmeldung schützen, was aber scheinbar in der Regel werkseitig deaktiviert ist.

Wenn dieser Port offen ist, könnte man sich einfach per ffplay einen solchen Stream anschauen:

ffplay rtsp://1.2.3.4/11

Wenn man sich nicht sicher ist, wie die korrekte RTSP-URL für die jeweilige IP-Kamera lautet, kann nmap zusammen mit dem Script rtsp-url-brute helfen:

nmap --script rtsp-url-brute -p 554 1.2.3.4

Die rechtliche Lage

Nun kann es natürlich rechtlich schwierig sein, bei einer fremden IP-Adresse nach dem offenen Port 554/TCP zu suchen, dort per nmap nach einer nutzbaren RTSP-URL zu scannen und sich den Stream dann live per ffplay, nmap oder vlc anzuschauen. Schließlich hat man nicht das Einverständnis des Betreibers.

Screenshot of shodan search engine, filtering for RTSP Streams.

Natürlich hält das wohl weniger Menschen ab, die ohnehin Schlechtes im Sinn haben. Ebenfalls gibt es verschiedene Dienste, die 24/7 nichts anderes tun. Ein Beispiel ist hier vielleicht shodan.io – dort lässt sich direkt nach solchen Vorschaubildern filtern, ohne dass man selbst eine Verbindung zu betroffenen IPs aufnehmen muss.

Warum ist das alles überhaupt problematisch?

Hat ein Angreifer Böses im Sinn, ist ein Zugriff auf die Überwachungskamera sehr hilfreich. Man kommt so möglicherweise an Insiderinformationen, findet heraus, wo wertvolle Dinge gelagert werden, wann und wie die Öffnungszeiten sind oder sogar mögliche Zugangscodes und Kennwörter. Natürlich auch, welche Kunden sich zu welcher Zeit dort aufhalten usw.

Denkt man an eine Arztpraxis, kann das schnell eine echte Datenschutzkatastrophe werden. Wenn die Kamera im Wohnzimmer oder Schlafzimmer einer Wohnung steht, führt das ebenfalls schnell zu ungewollten Einblicken.

Wenn man einmal außer Acht lässt, dass niemand gerne ohne sein Wissen per Livestream im Internet zu sehen ist, halte ich das Thema Datenschutz für eines der größten Risiken.

In der Vergangenheit sind mir bereits Beispiele begegnet, die das Problem verdeutlichen: Arztpraxen mit Kamerablick von hinten auf die Anmeldung – inklusive direktem Blick auf Patienten, Monitore mit Patientendaten oder Vertragsabschlüsse bei Mobilfunkanbietern. Auch Überwachungskameras in DHL-Filialen, die Bild und Ton in Zoom und 4K aufzeichnen, habe ich gesehen.

Für private Betreiber kann es ebenfalls schnell zu einem Datenschutzproblem werden. Nicht jeder achtet beim eingestellten Bildausschnitt der Kamera darauf, die Vorgaben des Datenschutzes einzuhalten. So werden oft mehr öffentliche Bereiche oder sogar Nachbargrundstücke gefilmt, als zulässig ist.

Wenn diese Daten dann auch noch ohne Schutz und Hürden in die Hände Dritter geraten, wird es heikel. Hier sollte besser jemand mit rechtlichem Hintergrund eine Einschätzung abgeben. Für mich klingt das alles jedenfalls ziemlich unschön.

Was kann man tun?

Eine Abuse-Mail an den jeweiligen ISP (Internet Service Provider) schicken, mit der Bitte, ihre Kunden zu informieren? Kann man machen. Bei kleineren ISPs klappt das oft sogar, und die Betreiber werden informiert. Spricht man aber über einen großen ISP wie die Telekom, verschwinden einzelne Abuse-Mails gefühlt einfach im Nichts.

Sonst jemanden zu finden, der ein Interesse daran hat, den meist unwissenden Betreiber zu informieren, ist nahezu unmöglich. Weder unsere Behörden noch das BSI interessieren sich dafür. Möchte man also den Betreiber darauf hinweisen, bleibt realistisch nur die Möglichkeit, ihn selbst zu kontaktieren.

GeoGuessr in der Praxis

Jetzt sind wir beim Thema GeoGuessr: Man hat also nur das Bild der Kamera, die IP-Adresse mit einer recht groben und nicht immer stimmigen Geolokalisierung und vielleicht noch ein paar weitere Rahmeninfos oder Dienste auf dieser IP-Adresse. Hin und wieder macht es mir daher sogar Spaß, den eigentlichen Betreiber ausfindig zu machen und ihm per E-Mail oder telefonisch kurz auf diesen möglichen Missstand hinzuweisen.

Wenn du das also gerade liest, weil ich dich darauf hingewiesen habe, weißt du jetzt, warum.

Natürlich trifft man oft auf Unverständnis – oder das klassisch deutsche „Anzeige ist raus!“ begegnet einem immer mal wieder. Es bietet also auch eine gute Möglichkeit, die eigenen Kommunikationsskills zu erweitern.

Siehe auch: IP-Kameras: Risiken und Checks

Fragen? Einfach melden.

BitLocker im Dual-Boot: Systemplatte auf Passwortschutz umstellen

Die Feiertage sind da, und ich hatte tatsächlich etwas Zeit zum Zocken. Gearbeitet wird unter Linux, gezockt unter Windows. Dafür habe ich mein Windows auf einer gesonderten SSD installiert, verschlüsselt mit BitLocker.

Illustration eines Dual-Boot-Systems mit Linux und Windows, dargestellt durch die Logos beider Betriebssysteme. Die Windows-Seite zeigt ein BitLocker-Schloss-Symbol, das auf die Verschlüsselung der Systemplatte hinweist.

Die SSD hat irgendwann aufgegeben, Windows musste neu drauf. Backup spare ich mir, ist eh nur zum Zocken. Windows 11, Treiber, Games, fertig. Aber: BitLocker fragt bei jedem Start nach dem Wiederherstellungsschlüssel, wenn ich vorher in Linux war. Das hatte ich schon mal, am gleichen Rechner. Damals nicht aufgeschrieben. Diesen Fehler mache ich nicht noch mal.

Das Problem

BitLocker mit TPM merkt, wenn sich die Boot-Kette ändert. Wechselt man über GRUB zwischen Linux und Windows, sieht das TPM eine Änderung und verlangt den Recovery Key. Die Lösung: TPM als Schlüsselschutz entfernen und stattdessen ein Passwort setzen. Dann fragt BitLocker bei jedem Start nach dem Passwort, egal ob vorher Linux oder Windows lief.

Gruppenrichtlinie anpassen

Damit Windows erlaubt, TPM vom Betriebssystemvolume zu entfernen, muss eine Gruppenrichtlinie geändert werden. Win + S, nach Gruppenrichtlinie bearbeiten suchen, dann:

ComputerkonfigurationAdministrative VorlagenBitLocker-LaufwerkverschlüsselungBetriebssystemlaufwerkeZusätzliche Authentifizierung beim Start anfordern

Editor für lokale Gruppenrichtlinien mit geöffneter Einstellung 'Zusätzliche Authentifizierung beim Start anfordern' unter 'Computerkonfiguration > Administrative Vorlagen > BitLocker-Laufwerkverschlüsselung > Betriebssystemlaufwerke'. Die Option ist aktiviert, um die BitLocker-Verschlüsselung ohne TPM zu ermöglichen.
Detailansicht der Gruppenrichtlinieneinstellung 'Zusätzliche Authentifizierung beim Start anfordern'. Die Option 'BitLocker ohne kompatibles TPM zulassen' ist aktiviert, und die Konfiguration für TPM-Start, Systemstartschlüssel und PIN wird angezeigt. Die Beschreibung auf der rechten Seite erläutert die Auswirkungen der Richtlinie.

Den Haken bei „BitLocker ohne kompatibles TPM zulassen“ setzen. Danach im Administrator-Terminal die Richtlinie anwenden:

gpupdate /force

Schlüsselschutz umstellen

Zuerst den aktuellen Status prüfen:

manage-bde -protectors -get C:

In meinem Fall waren drei Schlüsselschutzvorrichtungen konfiguriert: Numerisches Kennwort, TPM und PIN, sowie TPM. Alle müssen weg, damit nur noch das Passwort übrig bleibt.

Schritt für Schritt im Administrator-Terminal:

# 1. Alle Schutzvorrichtungen deaktivieren
manage-bde -protectors -disable C:
# 2. Vorhandene Schutzvorrichtungen anzeigen (IDs notieren)
manage-bde -protectors -get C:
# 3. Jede Schutzvorrichtung einzeln löschen (ID aus Schritt 2)
manage-bde -protectors -delete C: -id {TPM-UND-PIN-ID}
manage-bde -protectors -delete C: -id {TPM-ID}
manage-bde -protectors -delete C: -id {NUMERISCHES-KENNWORT-ID}
# 4. Prüfen, dass keine mehr da sind
manage-bde -protectors -get C:
# Erwartete Ausgabe: "Es wurden keine Schlüsselschutzvorrichtungen gefunden."
# 5. Passwort als neue Schutzvorrichtung hinzufügen
manage-bde -protectors -add C: -password
# (Passwort eingeben und bestätigen)
# 6. Schutz wieder aktivieren
manage-bde -protectors -enable C:

Ergebnis prüfen

manage-bde -status
Volume "C:" [System]
    Verschlüsselt (Prozent):      100,0 %
    Verschlüsselungsmethode:      XTS-AES 128
    Schutzstatus:                 Der Schutz ist aktiviert.
    Schlüsselschutzvorrichtungen:
        Kennwort

Nur noch Kennwort als Schutzvorrichtung. Beim nächsten Start fragt BitLocker nach dem Passwort, unabhängig davon ob vorher Linux oder Windows lief. GRUB stört nicht mehr.

Wer auf der Linux-Seite ebenfalls verschlüsselt: Bei LUKS kann ein falsches Tastaturlayout bei der Passphrase-Abfrage für Verwirrung sorgen.

Fragen? Einfach melden.

Kernel-Error jetzt auch im Tor-Netz: meine .onion-Adresse

Kurzfassung: www.kernel-error.de ist zusätzlich als Tor Hidden Service erreichbar.
Meine .onion-Adresse: jjyvff6eh3kp7ydfkamm27cldhsee2cl6wzfa5lfjyrfyribgeaesgqd.onion

Illustration: Kernel-Error Blog als HTTPS-Website und Tor Hidden Service (.onion) mit Fokus auf Privatsphäre, ohne Exit-Node und ohne DNS-Leaks

Der ursprüngliche Beitrag war ziemlich kurz gehalten. Seither werde ich immer wieder gefragt, wie das eigentlich zusammenspielt und was man bei so einem Setup alles bedenken muss. Also habe ich ihn deutlich erweitert und versuche, den Aufbau von der Idee bis zum fertigen vHost nachvollziehbar zu machen – inklusive der Stolperfallen, die ich unterwegs eingesammelt habe.

Warum überhaupt eine Onion-Variante?

Die Seite läuft ohnehin unter HTTPS mit aktuellen Cipher-Suites, Post-Quantum Key Exchange und HSTS. Technisch gesehen passiert zwischen Besucher und Server also schon heute nichts Unverschlüsseltes. Trotzdem gibt es ein paar Gründe, die für einen zusätzlichen Hidden Service sprechen:

  • Metadaten-Minimierung: Beim Clearnet-Abruf sieht der Provider mindestens, dass jemand mit meiner IP spricht. DNS-Resolver, Transit-Router und der Exit-Punkt kennen das Ziel. Bei einem Hidden Service ist außerhalb des Tor-Netzwerks gar nichts mehr zu sehen – weder IP noch DNS-Namen.
  • Kein Exit-Node: Wer meine Seite über einen normalen Tor-Browser mit Clearnet-URL besucht, spricht am Ende über einen Exit-Node mit meinem Server. Der Exit sieht zwar nur TLS, kann aber Metadaten wie SNI oder Zielhost mitbekommen und je nach Land auch gesperrt werden. Mit einer Onion-Adresse fällt der Exit-Node komplett weg.
  • Keine CA-Abhängigkeit: Die .onion-Adresse ist der Public-Key-Hash selbst. Wer die korrekte Adresse kennt, spricht kryptografisch nachweisbar mit meinem Server – ganz ohne Zertifikatsausstellerin und ohne OCSP-Kaskade.
  • Zensurresistenz: Sollte der Clearnet-Zugang aus irgendeinem Grund blockiert sein – falsche DNS-Antworten, gesperrte IP, generelle Sperre der Domain – bleibt die Onion-Variante unabhängig davon erreichbar.
  • Spielerei und Lerneffekt: Ich finde das Konzept hinter v3-Onions schlicht spannend. Ed25519-Keys, Rendezvous-Protokoll, Introduction Points – da steckt eine Menge gut durchdachte Krypto drin, die man am eigenen Dienst deutlich besser versteht als aus Diagrammen.

Wie das Ganze aufgebaut ist

Der Blog läuft in einer FreeBSD-Jail mit Nginx, PHP-FPM und einer eigenen Tor-Instanz. Für den Hidden Service sind drei Bausteine relevant:

  • Der Tor-Daemon hält das Schlüsselmaterial und den Rendezvous-Teil. Er lauscht nicht auf einer öffentlichen IP, sondern hängt sich in das Tor-Netzwerk und meldet dort den Dienst an.
  • Nginx stellt einen eigenen vHost bereit, der ausschließlich auf einer Loopback-Adresse (127.0.0.6:80) lauscht. Öffentlich erreichbar ist dieser vHost nie – er wird ausschließlich vom lokalen Tor-Prozess angesprochen.
  • PHP-FPM liefert wie gewohnt über einen Unix-Socket die WordPress-Inhalte aus – allerdings ohne FastCGI-Cache für den Onion-Pfad. Dazu gleich mehr.

Der Vorteil dieser Trennung: Alles, was der Tor-Daemon an den Nginx weiterreicht, kommt garantiert aus dem Tor-Netzwerk. Und alles, was der Clearnet-vHost ausliefert, geht garantiert nicht über das Onion-Interface. Zwei saubere Welten, gleiche Codebasis, null Überschneidung im Cache.

Tor-Daemon: torrc

Die minimale Konfiguration für einen v3-Hidden-Service ist überraschend kurz:

HiddenServiceDir      /var/db/tor/hidden_service/
HiddenServiceVersion  3
HiddenServicePort     80 127.0.0.6:80
SocksPort             127.0.0.6:9050
Log                   notice file /var/log/tor/notices.log

Beim ersten Start legt Tor in HiddenServiceDir das komplette Schlüsselmaterial selbst an: den privaten ed25519-Schlüssel, die daraus abgeleitete hostname-Datei mit der .onion-Adresse und ein paar weitere Dateien für die Introduction-Points. Diese Dateien sind privilegiert – wer sie hat, kann den Dienst übernehmen und sich gegenüber der Welt als mein Server ausgeben. Entsprechend gehören sie dem _tor-User, haben mode 700 und sind im Backup separat behandelt.

Die Adresse selbst ist 56 Zeichen lang und wird aus dem ed25519-Public-Key abgeleitet. Aenderbar ist sie nicht – wer eine bestimmte Wunschadresse will, muss mit Tools wie mkp224o so lange Schlüssel durchprobieren, bis der Base32-Prefix passt. Hat mich nicht gereizt, dafür ist meine Adresse eben zufällig.

Nginx-vHost für den Onion-Service

Der eigentliche vHost ist bewusst klein gehalten. Kein Cache, kein TLS, keine QUIC-Spielereien – alles, was spezifisch für den Clearnet-Teil ist, fehlt hier:

server {
    listen 127.0.0.6:80;
    server_name jjyvff6eh3kp7ydfkamm27cldhsee2cl6wzfa5lfjyrfyribgeaesgqd.onion;

    root  /usr/local/www/www.kernel-error.de;
    index index.php index.html index.htm;

    # access/error-logs per Default aus (keine Besucher-Spuren im Dateisystem).
    # Nur bei Bedarf zu Debugging-Zwecken aktivieren und später wieder aus.
    #access_log /var/log/nginx/www-kernel-error-de-access_tor.log combined;
    #error_log  /var/log/nginx/www-kernel-error-de-error_tor.log;

    add_header X-Robots-Tag      "noarchive, noimageindex" always;
    add_header Permissions-Policy "interest-cohort=()"    always;

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

    location ~* \.(css|gif|ico|jpeg|jpg|js|png|svg|webp|woff2?)$ {
        access_log off;
        expires 30d;
        add_header Cache-Control "public";
        try_files $uri =404;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php-fpm.sock;
        fastcgi_index index.php;

        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO       $fastcgi_path_info;

        # Cache hart deaktivieren - kein gemeinsamer Pool mit dem Clearnet-vHost
        fastcgi_no_cache      1;
        fastcgi_cache_bypass  1;
    }
}

Ein paar Punkte sind bewusst so gewählt:

  • listen 127.0.0.6:80: Die Adresse ist frei wählbar innerhalb von 127.0.0.0/8, muss aber mit dem HiddenServicePort in der torrc übereinstimmen. Ich nehme bewusst nicht 127.0.0.1, damit der Onion-Listener klar vom Standard-Loopback zu unterscheiden ist – hilft beim Debugging und bei sockstat.
  • Kein TLS: Zwischen Tor-Daemon und Nginx liegt nur die Loopback-Schnittstelle, dafür braucht es kein Zertifikat. Und .onion-Zertifikate gibt es nur über kommerzielle CAs (Extended Validation), Let’s Encrypt stellt keine aus. Der eigentliche Schutz auf dem Draht kommt komplett aus dem Tor-Protokoll – End-to-End verschlüsselt vom Tor-Client bis hier zur Loopback-Adresse.
  • Getrennte Logs (per Default aus): Im Normalbetrieb sind access_log und error_log im Onion-vHost auskommentiert – so entstehen erst gar keine Dateien mit IP-, User-Agent- oder Pfad-Informationen der Onion-Besucher. Zum Debugging lassen sich die beiden Zeilen kurz einkommentieren, danach bewusst wieder aus. Wichtig ist auf jeden Fall, dass Clearnet- und Onion-Zugriffe nie im gleichen Logfile landen – über Zeitstempel und User-Agent-Muster ließen sich sonst leicht Korrelationen bilden.
  • X-Robots-Tag: noarchive, noimageindex verhindert, dass Suchmaschinen die Onion-Variante cachen oder Bilder indexieren. noindex setze ich bewusst nicht – wer die Onion-Adresse kennt, darf sie auch in Verzeichnissen auftauchen lassen.
  • Permissions-Policy: interest-cohort=() schaltet Googles FLoC-/Topics-API explizit aus. Für einen Privacy-Blog im Tor-Netz ist das eher symbolisch, schadet aber nicht.

Der Knackpunkt: Cache-Isolation

Der Clearnet-vHost nutzt einen FastCGI-Cache (fastcgi_cache_path), damit WordPress nicht jede Seitenauslieferung neu rendern muss. Das ist für einen Blog mit statischen Inhalten ein riesiger Performance-Boost. Genau dieser Cache ist aber auch der Punkt, an dem man sich beim Onion-Betrieb ins Knie schießen kann.

Wenn der Onion-vHost denselben Cache-Pool verwenden würde, könnten zwei unerwünschte Effekte auftreten:

  • Cross-Origin-Leakage im HTML: WordPress baut absolute Links anhand der siteurl/home-Option. Die steht auf https://www.kernel-error.de. Wenn eine Clearnet-Anfrage cached, landen in jedem Link Clearnet-URLs im Cache-Eintrag. Liefert der Onion-vHost dann dieselbe Seite aus dem Cache, sieht der Tor-Besucher eine Seite voller Clearnet-Links – und jeder Klick würde ihn aus dem Hidden Service hinauskatapultieren.
  • Fingerprinting über Cache-Keys: Abhängig davon, wer die Seite zuerst aufgerufen hat, liefert der Cache deterministisch „warme“ oder „kalte“ Antworten. Ein Angreifer, der beide Varianten beobachtet, kann Rückschlüsse ziehen. Klein, aber unnötig.

Die Lösung ist bewusst stumpf: Der Onion-vHost bekommt gar keinen fastcgi_cache_path-Verweis. Zusätzlich stehen im location ~ \.php$-Block die beiden Schalter fastcgi_no_cache 1 und fastcgi_cache_bypass 1, damit selbst bei versehentlich geerbter Konfiguration weder gelesen noch geschrieben wird. Jeder Request rendert frisch durch den FPM.

Bei der Besucher-Zahl über den Hidden Service ist das unkritisch. Die Clearnet-Seite bleibt gleichzeitig durch ihren eigenen Cache schnell – beide Welten profitieren von ihrer jeweils passenden Strategie.

Der Onion-Location-Header und die Meldung im Tor Browser

Damit Besucher überhaupt erfahren, dass es eine Onion-Variante gibt, setzt der Clearnet-vHost zwei zusätzliche Header:

add_header Onion-Location "http://jjyvff6eh3kp7ydfkamm27cldhsee2cl6wzfa5lfjyrfyribgeaesgqd.onion$request_uri" always;
add_header Alt-Svc        'http="jjyvff6eh3kp7ydfkamm27cldhsee2cl6wzfa5lfjyrfyribgeaesgqd.onion:80"; ma=86400' always;

Der Onion-Location-Header ist ein vom Tor-Project definiertes Signal. Ruft man den Blog im Tor Browser (Version 9.5 oder neuer) über die Clearnet-Adresse auf, blendet der Browser eine unauffällige „.onion available“-Schaltfläche in der URL-Leiste ein und bietet an, auf die Hidden-Service-Variante umzuschalten. Das ist die „Meldung“, die einige irritiert: Sie kommt nicht von meiner Seite, sondern direkt vom Tor Browser, der den Header interpretiert.

Wichtig dabei: Der Tor Browser akzeptiert den Header nur, wenn ein paar Bedingungen erfüllt sind. Das verhindert, dass ein kompromittierter Server Besucher auf fremde Hidden Services umlenken kann:

  • Die Ursprungsseite muss per HTTPS ausgeliefert worden sein.
  • Die im Header angegebene .onion-Adresse muss wohlgeformt sein (v3, 56 Zeichen, Base32).
  • Die Nutzerin muss die Umleitung aktiv bestätigen – es gibt keine automatische Weiterleitung.

Der Alt-Svc-Header (RFC 7838) ist die generische Variante. Einige alternative Clients nutzen ihn, um Alternative-Services im Hintergrund vorzumerken. Für den Tor Browser ist primär der Onion-Location-Header relevant, Alt-Svc ist eher Absicherung.

DNS als zusätzliche Verifikation

Ein Header auf der Seite ist eine gute Sache – wer dem Server vertraut, bekommt den Hinweis auf den Onion-Service. Was aber, wenn jemand meine Angabe kontrollieren will, ohne die Seite selbst aufgerufen zu haben?

Dafür habe ich einen einfachen TXT-Record im DNS hinterlegt:

dig +short TXT www.kernel-error.de
"onion=jjyvff6eh3kp7ydfkamm27cldhsee2cl6wzfa5lfjyrfyribgeaesgqd.onion"

Das Format onion=… ist kein offizieller Standard, sondern meine persönliche Konvention. Es gibt einen Internet-Draft von Alec Muffett (draft-muffett-same-origin-onion-v2), der einen ähnlichen Ansatz beschreibt, aber als RFC nie durchgegangen ist. Bis sich etwas Standardisiertes etabliert, bleibe ich bei meinem einfachen Ansatz: Wer wissen will, ob die Onion-Adresse wirklich zu mir gehört, vergleicht den Header, den TXT-Record und meine öffentlichen Ankündigungen. Die Zone selbst ist DNSSEC-signiert, der TXT-Record ist also nicht spürbar manipulierbar, solange der Resolver DNSSEC validiert.

Wem das zu wacklig ist: Für .onion gibt es auch kommerzielle Zertifikate (DigiCert stellt EV-Zertifikate für Onion-Services aus). Damit könnte man die Onion-Seite zusätzlich hinter HTTPS legen und in der Tor-Browser-URL-Leiste wäre die Organisation sichtbar. Für einen privaten Blog ist der Aufwand und die Kosten aber deutlich höher als der Nutzen.

Warum kein TLS-Zertifikat nötig ist

Der Punkt irritiert am Anfang fast jeden: Eine Webseite ohne HTTPS, im Jahr 2026, fühlt sich einfach falsch an. Wenn man sich aber anschaut, wofür TLS im Clearnet eigentlich da ist, wird schnell klar, warum es bei einem Tor Hidden Service tatsächlich überflüssig ist.

TLS erfüllt zwei Aufgaben gleichzeitig:

  • Vertraulichkeit auf der Leitung: Niemand zwischen Client und Server soll mitlesen können – weder der ISP, noch das WLAN im Cafe, noch ein Transit-Provider.
  • Identitätsnachweis des Servers: Der Client muss sicher sein können, dass er tatsächlich mit dem Server spricht, der hinter dem Domainnamen steht. Diese Aufgabe übernimmt die CA-Kette, die das Zertifikat an den DNS-Namen bindet.

Bei einem Tor Hidden Service sind beide Aufgaben bereits in das Protokoll selbst eingebaut – ohne dass irgendwo ein Zertifikat im Spiel wäre:

  • Vertraulichkeit kommt aus dem Tor-Tunnel: Der Datenstrom zwischen Client und Hidden Service lauft durch mehrere übereinander gelegte Verschlüsselungsschichten. Jedes Relay kann nur die eigene Schicht entfernen und sieht dadurch nur das nächste Ziel, nicht den Inhalt und auch nicht den gesamten Pfad. Auf dem Draht ist der Traffic zu keinem Zeitpunkt im Klartext zu sehen – ein zusätzliches TLS würde dieselbe Eigenschaft nur ein zweites Mal liefern.
  • Authentizität steckt in der Adresse: Die 56 Zeichen einer v3-Onion sind keine willkürliche Zeichenfolge, sondern die Base32-Darstellung eines ed25519-Public-Keys (plus Checksumme und Versionsbyte). Beim Verbindungsaufbau fordert der Tor-Client vom Server eine Signatur an und prüft sie gegen genau diesen Public-Key. Passt sie nicht, kommt keine Verbindung zustande. Damit übernimmt die Adresse selbst die Rolle, die im Clearnet das Zertifikat spielt – nur dass hier keine CA existieren muss, die das beglaubigt.

Anders formuliert: Der Hidden Service ist von der ersten Byte an selbst-authentisierend. Die Sicherheitsgarantie steht und fällt mit der korrekten Onion-Adresse, nicht mit einem Dritten, der sie beglaubigen müsste. Deshalb hat Let’s Encrypt für .onion-Adressen bewusst nie Zertifikate ausgestellt – es gibt schlicht nichts, was eine CA hier noch verifizieren könnte, was nicht schon durch die Adresse selbst belegt wäre.

Den einzigen verbleibenden Nutzen eines Zertifikats – das sichtbare Organisationskürzel in der Tor-Browser-URL-Leiste bei EV-Zertifikaten – kann man sich für einen privaten Blog guten Gewissens sparen.

Firewall und Systemhärtung

Die schöne Eigenschaft eines Hidden Service: Es muss kein zusätzlicher Port nach außen geöffnet werden. Der Tor-Prozess baut nur ausgehende Verbindungen auf, der Nginx-Onion-vHost lauscht nur auf der Loopback-Adresse. Aus Sicht einer Firewall ändert sich durch den Hidden Service gar nichts.

Trotzdem gibt es ein paar Punkte, die ich bewusst setze:

  • Jail-Isolation: Tor und Nginx laufen in derselben FreeBSD-Jail. Die Jail hat nur die öffentliche IP für den Clearnet-Nginx und separate Loopback-Adressen für interne Dienste. Dadurch kann der Tor-Prozess nicht „aus Versehen“ auf andere Dienste zugreifen – und er teilt sich seinen Kernel-Namespace nur mit dem, was ich bewusst in dieselbe Jail gelegt habe.
  • Dateirechte: /var/db/tor/hidden_service/ gehört dem _tor-User, mode 700. Das private Schlüsselmaterial muss genauso behandelt werden wie ein TLS-Private-Key – wer es hat, kann meine .onion übernehmen.
  • WordPress-Härtung: Der Login (wp-admin, wp-login.php) wird über das Plugin WPS Hide Login auf eine nicht-offensichtliche URL gelegt und ist ohnehin nur via Clearnet mit HTTPS erreichbar. Im Onion-vHost wird die Login-URL weder verlinkt noch erwähnt. Administration findet ausschließlich über die Clearnet-Variante statt.
  • Keine Log-Korrelation: Die getrennten Access-Logs habe ich schon erwähnt. Zusätzlich analysiere ich sie separat und führe keine IPs aus dem Clearnet-Log mit dem Onion-Log zusammen. Das wäre technisch möglich, würde aber den Sinn des Setups konterkarieren.
  • Keine externen Ressourcen: Das Theme und die eingebundenen Plugins laden keine Fonts von Google, keine Scripts von CDNs, keine Gravatare aus der Welt. Wenn ein Element doch mal nach www.kernel-error.de auflaufen sollte (etwa durch ein falsch konfiguriertes Plugin), würde der Tor Browser das als Mixed-Origin-Request anzeigen und viele Nutzerinnen würden es nicht mehr laden – das fällt dann schnell auf und ich fixe es.

Warum das als „sicher“ gilt

„Sicher“ ist immer relativ – gegenüber welchem Angreifer, unter welchem Modell? Ein paar Eigenschaften kann man aber konkret benennen:

  • End-to-End verschlüsselt: Tor baut zwischen Client und Hidden Service einen dreistufigen Tunnel durch Rendezvous- und Introduction-Points. Keiner der Zwischenknoten sieht, wer mit wem spricht oder was übertragen wird. Auf dem Draht sieht außerdem niemand eine .onion-Adresse – die Rendezvous-Logik wählt den Zielknoten indirekt.
  • Kryptografische Authentizität der Adresse: Die 56 Zeichen der v3-Onion sind der Base32-codierte ed25519-Public-Key samt Checksumme und Versionsbyte. Wer die richtige Adresse getippt oder kopiert hat, landet kryptografisch garantiert auf dem Server, der den zugehörigen privaten Schlüssel hat. Keine CA, kein DNS, kein Zwischenhändler kann das verändern, ohne dass die Adresse anders aussieht.
  • Kein TLS nötig: Verschlüsselung kommt aus dem Tor-Tunnel, Authentizität aus der Adresse selbst. Details dazu stehen weiter oben im eigenen Abschnitt – kurz: TLS würde die Eigenschaften nicht ergänzen, sondern nur doppeln.
  • Anonymität der Besucher: Der Server sieht keine echte Client-IP, nur einen Rendezvous-Punkt im Tor-Netz. Zensur und Traffic-Analyse auf dem letzten Stück entfallen vollständig.
  • Vertraulichkeit für mich: Der Server ist nicht öffentlich erreichbar, es gibt keinen offenen Port, den man mit nmap finden könnte. Die öffentliche IP des Servers ist durch die Tor-Rendezvous-Logik nicht aus dem Netzwerkverkehr ableitbar, solange ich nicht durch Fehlkonfiguration (etwa hart verlinkte Clearnet-URLs im HTML) Hinweise leake.

Stolperfallen und was man besser lässt

  • WordPress-URLs: siteurl und home bleiben auf der Clearnet-URL. Einige Anleitungen empfehlen, bei Aufruf der Onion-Variante die URL dynamisch umzuschreiben. Das habe ich bewusst nicht gemacht – es führt zu kaputten Canonical-Links, mischt Content- und Admin-Bereich und bricht meist den Blöck-Editor. Stattdessen akzeptiere ich, dass im Onion-HTML auch mal eine Clearnet-Link vorkommt (etwa im Footer), und setze darauf, dass der Tor Browser korrekt warnt, bevor er dort hinschickt.
  • Externe Embeds: YouTube-Videos, Twitter-Embeds, Gravatare – alles Dinge, die eine Onion-Seite sofort deanonymisieren könnten, wenn sie geladen würden. Das Theme und die Plugins auf diesem Blog laden bewusst keine externen Ressourcen.
  • Redis/Object-Cache: Der Object-Cache speichert keine gerenderten HTML-Seiten, sondern nur WP-interne Objekte. Hier ist die Vermischung unkritisch, weil die resultierenden URLs erst beim Rendern (durch den jeweiligen vHost) entstehen.
  • FastCGI-Cache: Wie oben beschrieben – für die Onion-Variante komplett deaktiviert. Wer es trotzdem aktivieren will, braucht zwingend einen eigenen fastcgi_cache_path, eigenes Key-Schema und muss den Host-Teil im Cache-Key haben.
  • Monitoring: Klassische Uptime-Checks von außen funktionieren auf .onion-Adressen nicht ohne weiteres. Dienste wie Uptime-Kuma können inzwischen Tor-Proxy nutzen, ansonsten hilft ein kleiner curl --socks5-hostname-Check.

Was man noch härter machen kann

Für meinen privaten Blog halte ich das beschriebene Setup für ein solides Minimum. Wer seinen Hidden Service strenger absichern will – etwa weil dort Whistleblower-Material, Redaktions-Inhalte oder andere wirklich schutzwürdige Dinge liegen – sollte ein paar zusätzliche Punkte beachten:

  • Zusätzliche HTTP-Header: Referrer-Policy: no-referrer verhindert, dass beim Klick auf einen externen Link die .onion-URL als Referer mitgeschickt wird. Content-Security-Policy (restriktiv, z. B. default-src 'self'; img-src 'self' data:;) blockt Mixed-Origin-Requests hart, bevor der Browser sie überhaupt versucht. Dazu X-Content-Type-Options: nosniff und X-Frame-Options: DENY, damit Clickjacking und MIME-Sniffing ausgeschlossen sind.
  • server_tokens off: Im Haupt-vHost ist der Nginx-Version-String schon länger abgeschaltet und durch einen eigenen Custom-Header ersetzt. Im Onion-vHost gehört server_tokens off; genauso rein – ohne das steht die Nginx-Version in jeder 404-Seite und erleichtert Fingerprinting.
  • Tor-Daemon härten: Sandbox 1 in der torrc aktiviert unter FreeBSD Capsicum und unter Linux Seccomp-Filter. Damit bekommt der Tor-Prozess nur die Syscalls, die er wirklich braucht. Zusätzlich lässt sich mit HiddenServiceEnableIntroDoSDefense 1 sowie HiddenServiceEnableIntroDoSRatePerSec und HiddenServiceEnableIntroDoSBurstPerSec ein integrierter DoS-Schutz am Introduction-Point aktivieren – seit Tor 0.4.2 gibt es das als Plugin-freie Bordmittel.
  • Client Authorization: Für wirklich nicht-öffentliche Dienste kennt Tor einen Mechanismus, bei dem nur Clients mit passendem x25519-Key-Paar den Hidden Service überhaupt erreichen. Die Adresse ist dann zwar im Tor-Netz bekannt, ohne die Private-Key-Datei auf dem Client kommt man aber keinen Zentimeter weit. Für einen öffentlichen Blog unpassend, für ein Journalisten-Dropbox-Setup die wichtigste Absicherung überhaupt.
  • Offline-Backup der Hidden-Service-Keys: Wenn der Inhalt von /var/db/tor/hidden_service/ verloren geht, ist die .onion-Adresse weg – es gibt keinen Weg, sie wiederherzustellen. Das private Schlüsselmaterial gehört deshalb auf einen verschlüsselten Offline-Datenträger und sollte genauso behandelt werden wie ein TLS-Root-Key. Wer die Adresse überträgt, überträgt gleichzeitig die Fähigkeit, sie zu betreiben – das ist nichts, was in einem Cloud-Backup liegen sollte.
  • Jail-/Container-Trennung von Tor und Web: Aktuell laufen Tor-Daemon und Nginx in derselben FreeBSD-Jail, weil sie über die Loopback-Adresse sowieso miteinander sprechen müssen. Wer paranoider sein will, packt den Tor-Prozess in eine eigene Jail mit eigener Loopback-IP und forwardet nur den HiddenService-Port – dann kann ein kompromittierter WordPress-Prozess nicht mal versehentlich an die Schlüsseldateien. Für mich persönlich ist der Aufwand zurzeit nicht gerechtfertigt, für einen Hochrisiko-Service aber eine ernsthafte Option.
  • Ehrliche Einschätzung zur Anonymität des Betreibers: Wer einen Hidden Service betreibt, um die eigene Identität zu schützen, muss auch alles drumherum sauber haben – Domain-Whois, Zertifikats-SANs auf dem Clearnet-Host, Uptime-Monitoring, Backups, Zeitstempel in Git-Commits, selbst die Systemzeit auf dem Server. Die .onion-Adresse alleine macht den Betreiber nicht anonym. Sie ist ein Baustein, kein Gesamtkonzept.

Für diesen Blog ist das bewusst nicht alles umgesetzt. Ich veröffentliche unter Klarnamen und möchte nur die Daten meiner Besucher besser schützen. Für jemanden, der aus guten Gründen wirklich anonym bleiben muss, sind die Punkte oben Pflicht, nicht Kür.

Fazit

Ein Tor Hidden Service für einen bestehenden WordPress-Blog ist überraschend geradlinig aufzusetzen. Die technische Umsetzung umfasst im Kern einen Tor-Daemon mit v3-Konfiguration, einen getrennten Nginx-vHost ohne HTTPS und ohne FastCGI-Cache, sowie zwei Header im Clearnet-vHost. Der Aufwand bleibt überschaubar, der Gewinn an Erreichbarkeit und Metadaten-Minimierung ist messbar.

Wer die Adresse im Tor Browser aufruft, bekommt exakt den gleichen Blog zu sehen – nur ohne TLS-Handshake, ohne Exit-Node und ohne Spur im eigenen DNS-Resolver. Die Seite wird nicht häufig aus dem Tor-Netz aufgerufen, aber sie ist für die Fälle da, in denen sie gebraucht wird. Und ehrlich gesagt: Es macht auch einfach Spaß, ein System so zu bauen, dass beide Welten sauber nebeneinander existieren.

Siehe auch: HTTP/3 und QUIC, Post-Quantum TLS für Nginx, TLS-ECDHE einfach erklärt

Fragen? Einfach melden.

WordPress wp-cron.php: Ist die angebliche Sicherheitslücke real?

Picture of an hacker checken for wordpress vulnerability

In letzter Zeit begegnen mir immer wieder sogenannte „Vulnerability Report Scams“. Klar, mit Angst und Unwissenheit kann man Geld verdienen, also wird es auch jemand tun. Besonders fällt mir das im Zusammenhang mit der wp-cron.php auf.

Ich habe häufig Reports gesehen, die in etwa so aussehen:

Critical Vulnerability Report- {Critical BUG #P1} - https://www.example.com/ - vulnerable to attack via wp-cron.php

Hello  Security team,

I am a Security Engineer, Cyber Security Researcher, Bug Bounty Hunter  & Ethical Hacker. While testing your domain https://www.example.com/ I have found some important vulnerabilities in your site.

Vulnerability Name:   https://www.example.com/  -  vulnerable to DoS attack via wp-cron.php

Vulnerable Domain:  https://www.example.com/wp-cron.php

Description:

The WordPress application is vulnerable to a Denial of Service (DoS) attack via the wp-cron.php script. This script is used by WordPress to perform scheduled tasks, such as publishing scheduled posts, checking for updates, and running plugins.
An attacker can exploit this vulnerability by sending a large number of requests to the wp-cron.php script, causing it to consume excessive resources and overload the server. This can lead to the application becoming unresponsive or crashing, potentially causing data loss and downtime.

I found this vulnerability at https://www.example.com/wp-cron.php endpoint.

Steps to Reproduce: reference- https://hackerone.com/reports/1888723

navigate to: https://www.example.com/wp-cron.php
intercept the request through the burp suite
right click on the request and send it to the repeater
Now send a request, and you will see the response as  200 OK

---

this can be also done by the curl command given below

curl -I "https://www.example.com/wp-cron.php"

POC: Attached

Impact:

If successful, this misconfigured wp-cron.php file can cause lots of damage to the site, such as:

Potential Denial of Service (DoS) attacks, resulting in unavailability of the application.
Server overload and increased resource usage, leading to slow response times or application crashes.
Potential data loss and downtime of the site.
Hackers can exploit the misconfiguration to execute malicious tasks, leading to security breaches.

Exploitation:
Exploitation can be done through a GitHub tool called doser.go https://github.com/Quitten/doser.go
I did not do that as this can impact your website.
Get the doser.py script at https://github.com/Quitten/doser.py
Use this command to run the script: python3 doser.py -t 999 -g 'https://www.example.com/wp-cron.php'
Go after https://www.example.com/ 1000 requests of the doser.py script.
The site returns code 502.

Suggested Mitigation/Remediation Actions:

To mitigate this vulnerability, it is recommended to disable the default WordPress wp-cron.php script and set up a server-side cron job instead. Here are the steps to disable the default wp-cron.php script and set up a server-side cron job:
Access your website's root directory via FTP or cPanel File Manager.
Locate the wp-config.php file and open it for editing.
Add the following line of code to the file, just before the line that says "That's all, stop editing! Happy publishing.":

1define('DISABLE_WP_CRON', true);

Save the changes to the wp-config.php file.
Set up a server-side cron job to run the wp-cron.php script at the desired interval. This can be done using the server's control panel or by editing the server's crontab file.
References:

For more information about this vulnerability, please refer to the following resources:

https://hackerone.com/reports/1888723

https://medium.com/@mayank_prajapati/what-is-wp-cron-php-0dd4c31b0fee

Cron
Fix Them ----- I have protected your company and saved it from a big loss so give me some appreciation Bounty Reward. I am sharing my PayPal ID with you. Paypal ID: woop woop Current Market Value Minimum Bounty Reward for Critical BUG P1 Type. The bug I reported is part of type P1 Vulnerability severity Bug bounty reward amount (in USD) P1 (Critical) $2500 P2 (High) $1500 P3 (Medium) $1000 P4 (Low) $500 Please feel free to let me know if you have any other questions or need further information. I am happy to secure it. I hope this will be fixed soon. Feel free to let me know if you have any other questions. Thanks & Regards

Ist das nun ein echtes Problem oder nicht?

Ja und Nein. In der Nachricht wird korrekt beschrieben, was die wp-cron.php tut und warum sie wichtig ist. Auch die Tatsache, dass sie extern unendlich oft aufgerufen werden kann und dadurch potenziell eine Überlastung auslösen könnte, ist nicht falsch. Selbst der Tipp, auf eine lokale Crontab-Version umzusteigen, ist nicht verkehrt. Allerdings muss man das Ganze in den richtigen Kontext setzen: wp-cron.php ist standardmäßig in WordPress aktiviert und wird für geplante Aufgaben genutzt. Die geplanten Aufgaben werden in der Datenbank abgelegt. Gibt es etwas zu tun und die wp-cron.php wird aufgerufen, dann wird auch gearbeitet. Gibt es nichts zu tun, dann gibt es auch keine Arbeit. Die Empfehlung, sie zu deaktivieren und durch einen serverseitigen Cron-Job zu ersetzen, ist eher eine Performance-Optimierung als eine echte Sicherheitsmaßnahme.

Es handelt sich nicht um einen Zero-Day-Exploit und es gibt keine direkte Gefahr eines Datenabflusses. Falls es wirklich zu Performance-Problemen kommt, gibt es einfache Gegenmaßnahmen. Sollte tatsächlich jemand versuchen, die wp-cron.php gezielt anzugreifen, hilft ein simples Rate Limiting, entweder über die Firewall oder direkt mit mod_security (Apache) bzw. limit_req (nginx).

Rate Limiting mit nginx

limit_req_zone $binary_remote_addr zone=cronlimit:10m rate=1r/s;

server {
    location = /wp-cron.php {
        limit_req zone=cronlimit burst=3 nodelay;
        limit_req_status 429;
    }
}

Das begrenzt die Anfragen auf 1 pro Sekunde, mit maximal 3 Anfragen in kurzer Zeit.

Sollte man wp-cron.php deaktivieren?

Nicht unbedingt. Klar, im Fall eines Angriffs kann das als erste Maßnahme helfen. Besser ist es aber, wp-cron.php lokal auszuführen und den Zugriff darauf über den Webserver auf bestimmte IP-Adressen zu beschränken. Anschließend kann man einen Cronjob anlegen, der alle 15 Minuten ausgeführt wird:

*/15 * * * * wget -q -O - https://www.example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1

Zugriff per nginx einschränken:

location ~* ^/wp-cron.php$ {
    allow 1.2.3.4;  # Ersetze mit deiner IP
    deny all;
}

Fazit

Das ist ganz sicher kein P1-Bug. Und wenn der Report direkt eine Preistabelle mitliefert, ist das schon ein ziemlich eindeutiges Zeichen für einen Scam.

  • Ja, wp-cron.php könnte unter bestimmten Umständen zu Problemen führen.
  • Nein, es ist kein echter Sicherheits-Bug.
  • Wer weiß, was er tut, hat bereits die richtigen Maßnahmen getroffen.

Keine Panik. Stattdessen lieber kurz die eigene Konfiguration prüfen und gut ist. Wer auf das nginx Rate Limit setzt und dieses testen möchte, kann mein rate_limit_test.sh auf GitHub nutzen.

Siehe auch: Ist mein Netzwerk kompromittiert?

Fragen? Einfach melden.

FreeBSD SSH-Server absichern: MFA mit Google Authenticator einrichten​

SSH-Keys sind der Standard. Aber manchmal lässt es sich nicht vermeiden, dass ein Login nur mit Benutzername und Kennwort abgesichert ist. Um das aufzuwerten, lässt sich der SSH-Server mit einem zweiten Faktor ausstatten — hier mit dem Google Authenticator (TOTP) auf FreeBSD.

Installation

pkg install pam_google_authenticator

PAM-Konfiguration

In /etc/pam.d/sshd das Google Authenticator PAM-Modul als zweiten required-Eintrag nach pam_unix einfügen:

#
# PAM configuration for the "sshd" service
#

# auth
auth		required	pam_unix.so		no_warn try_first_pass
auth		required	/usr/local/lib/pam_google_authenticator.so

# account
account		required	pam_nologin.so
account		required	pam_login_access.so
account		required	pam_unix.so

# session
session		required	pam_permit.so

# password
password	required	pam_unix.so		no_warn try_first_pass

Die Reihenfolge ist wichtig: Erst das Kennwort (pam_unix), dann der TOTP-Code. Auf dem gleichen Weg lässt sich MFA auch für su, den Konsolen-Login oder SSH-Keys einrichten — einfach das entsprechende PAM-File anpassen.

sshd_config anpassen

In /etc/ssh/sshd_config muss Challenge-Response aktiviert sein:

# Seit OpenSSH 8.7 heißt die Option KbdInteractiveAuthentication
# ChallengeResponseAuthentication ist ein Alias und funktioniert weiterhin
KbdInteractiveAuthentication yes

Danach service sshd restart — aber vorher sicherstellen, dass man noch eine offene Session hat, falls etwas nicht stimmt.

Authenticator einrichten

Auf dem Smartphone den Google Authenticator installieren (oder eine andere TOTP-App wie Aegis, 2FAS oder den Microsoft Authenticator). Dann auf dem Server mit dem gewünschten Benutzer google-authenticator aufrufen:

cd ~
google-authenticator

Das Tool zeigt einen QR-Code im Terminal, den man mit der Authenticator-App scannt:

Danach den angezeigten Code einmal eingeben — fertig. Bei jedem Kennwort-Login wird jetzt zusätzlich der aktuelle TOTP-Code abgefragt.

Wichtig: Das Tool zeigt auch Backup-Codes an. Diese unbedingt sicher aufbewahren — wenn das Smartphone verloren geht, kommt man sonst nicht mehr rein. Die Konfiguration liegt in ~/.google_authenticator und kann dort auch nachträglich eingesehen werden.

Siehe auch: FreeBSD OpenSSH: OS-Banner sicher entfernen, SSH-Bruteforce, DigitalOcean und AbuseIPDB – warum Blocken das Problem nicht löst

Unter Linux ist die Einrichtung sehr ähnlich — das PAM-Modul heißt dort libpam-google-authenticator. Fragen? Einfach melden.

DNSSEC und SSHFP unter Linux Mint und Ubuntu zum Laufen bringen

Heute habe ich versucht, mich von meiner neuen Linux Mint Installation aus mit einem meiner SSH-Server zu verbinden. Mein SSH-Client hat mich direkt gefragt, ob ich dem Hostkey vertrauen möchte:

ssh username@hostname.kernel-error.org
The authenticity of host 'hostname.kernel-error.org (2a01:5a8:362:4416::32)' can't be established.
ED25519 key fingerprint is SHA256:kTRGVCMRLiHfvJunW2CbW5H3NZmn3Wkx2KnHJXl3iJu.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?

Für viele ist das normal — man tippt „yes“ und sieht die Meldung nie wieder. Aber diese Meldung hat ihren Grund. Beim ersten Verbindungsaufbau zeigt SSH den Fingerprint des Server-Hostkeys an, damit man prüfen kann, ob man wirklich mit dem richtigen Server spricht und nicht mit einem Angreifer. Wer eh immer „yes“ sagt, könnte den Check auch gleich in seiner ~/.ssh/config abschalten:

Host *
    StrictHostKeyChecking no

SSHFP — Hostkeys per DNS verifizieren

Es gibt einen besseren Weg: SSHFP-Records (RFC 4255). Man hinterlegt die Fingerprints der erwarteten Hostkeys als DNS-Einträge. Der SSH-Client prüft diese automatisch — vorausgesetzt die DNS-Antwort ist per DNSSEC abgesichert. In der ~/.ssh/config:

Host *
   VerifyHostKeyDNS yes

Meine DNS-Server unterstützen alle DNSSEC, mein lokaler Resolver auf dem Router auch, die SSH-Config stimmt — und trotzdem erscheint die Meldung. Also mit ssh -vvv debuggen:

debug1: found 2 insecure fingerprints in DNS

Insecure. SSH findet die SSHFP-Records, vertraut ihnen aber nicht, weil die DNS-Antwort nicht als DNSSEC-validiert markiert ist.

Das Problem: systemd-resolved

Schneller Test mit dig +dnssec gegen Google DNS:

dig +dnssec hostname.kernel-error.org @8.8.8.8
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

Das ad-Flag (Authenticated Data) ist gesetzt — meine DNS-Server liefern DNSSEC korrekt aus. Auch der lokale Router-Resolver liefert ad. Aber ohne expliziten @server:

dig +dnssec hostname.kernel-error.org
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

Kein ad. Was steht in /etc/resolv.conf? 127.0.0.53systemd-resolved. Der Stub-Resolver von systemd schluckt das AD-Flag.

Man könnte in /etc/systemd/resolved.conf einfach DNSSEC=yes setzen — bei mir ging danach aber gar keine DNS-Auflösung mehr. Das liegt am Stub-Resolver, den man ebenfalls umkonfigurieren müsste. Nennt mich oldschool, aber für meine Zwecke reicht der klassische Weg über die vom NetworkManager gepflegte resolv.conf.

Lösung: systemd-resolved abschalten

sudo systemctl disable systemd-resolved
sudo systemctl stop systemd-resolved
sudo rm /etc/resolv.conf

In /etc/NetworkManager/NetworkManager.conf in der [main]-Sektion:

dns=default

NetworkManager neu starten:

sudo systemctl restart NetworkManager
cat /etc/resolv.conf
# Generated by NetworkManager
search kernel-error.local
nameserver 10.10.88.1
nameserver fd00:424e:6eff:f525:454e:6eff:f525:4241

DNS-Auflösung geht. Aber SSH sagt weiterhin „insecure“. Es fehlen noch zwei Optionen in der resolv.conf.

edns0 und trust-ad

Erste Erkenntnis — edns0 muss aktiviert sein, damit DNSSEC-Daten überhaupt transportiert werden. In /etc/resolv.conf:

options edns0

Jetzt zeigt dig das ad-Flag. Aber SSH sagt immer noch „insecure“. Warum? Ein Blick in den SSH-Quellcode — die ldns-Bibliothek macht die DNSSEC-Validierung:

        /* Check for authenticated data */
        if (ldns_pkt_ad(pkt)) {
                rrset->rri_flags |= RRSET_VALIDATED;
        } else { /* AD is not set, try autonomous validation */
                ldns_rr_list * trusted_keys = ldns_rr_list_new();
                /* ... */
                if ((err = ldns_verify_trusted(ldns_res, rrdata, rrsigs,
                     trusted_keys)) == LDNS_STATUS_OK) {
                        rrset->rri_flags |= RRSET_VALIDATED;
                }
        }

ldns prüft das AD-Flag im DNS-Paket. Aber die glibc setzt das AD-Flag in der Antwort nur dann, wenn trust-ad in der resolv.conf steht — sonst wird es aus Sicherheitsgründen herausgefiltert. Die vollständige Option:

options edns0 trust-ad

Und jetzt:

ssh username@hostname.kernel-error.org -vvv
[...]
debug1: found 2 secure fingerprints in DNS
debug3: verify_host_key_dns: checking SSHFP type 4 fptype 1
debug1: verify_host_key_dns: matched SSHFP type 4 fptype 1
debug3: verify_host_key_dns: checking SSHFP type 4 fptype 2
debug1: verify_host_key_dns: matched SSHFP type 4 fptype 2
debug1: matching host key fingerprint found in DNS

secure statt insecure. SSH verifiziert den Hostkey automatisch per DNSSEC — keine manuelle Fingerprint-Prüfung mehr nötig.

Rebootfest machen

Die manuell eingetragenen Optionen in der resolv.conf überleben keinen Reboot — der NetworkManager überschreibt die Datei. Per nmcli die Optionen dauerhaft im Netzwerkprofil setzen, für IPv4 und IPv6:

nmcli conn modify DEINE-PROFIL-UUID ipv4.dns-options edns0,trust-ad
nmcli conn modify DEINE-PROFIL-UUID ipv6.dns-options edns0,trust-ad

Die UUID des aktiven Profils findet man mit nmcli conn show. Beide Zeilen sind nötig — fehlt eine, greift es nicht.


Zusammenfassung: systemd-resolved unter Linux Mint und Ubuntu filtert das DNSSEC-AD-Flag heraus. Ohne AD-Flag kann SSH die SSHFP-Records nicht als vertrauenswürdig einstufen. Lösung: systemd-resolved abschalten, NetworkManager mit dns=default nutzen, edns0,trust-ad per nmcli setzen.

Wer einen DNSSEC-validierenden Resolver sucht — dns.kernel-error.de ist ein öffentlicher DNS-Resolver mit DNSSEC, DNS over TLS und DNS over HTTPS.

Und die offene Frage: Ich bin mit meinem FreeBSD-Wissen an das Thema gegangen. Wie macht man das als Linux-User mit systemd-resolved richtig? Schreibt mir, wenn ihr es wisst.

Siehe auch: SSH Host Keys per SSHFP

« Ältere Beiträge Neuere Beiträge »

© 2026 -=Kernel-Error=-RSS

Theme von Anders NorénHoch ↑