Terug naar Blog

Malafide TLS-certificaten op gameservers: hoe Certificate Transparency-logs betrappen wat firewalls missen

Gepubliceerd op 14 augustus 2026
Malafide TLS-certificaten op gameservers: hoe Certificate Transparency-logs betrappen wat firewalls missen Gegenereerd met behulp van AI

Kort samengevat

Monitor Certificate Transparency-logs om ongeautoriseerde TLS-certificaten op je game-backend te detecteren voordat ze een beveiligingsincident worden

Op dit moment is er een openbaar, append-only logbestand van elk TLS-certificaat dat is afgegeven voor elk domein op het internet — ook voor het jouwe. Als iemand morgen naar een Certificate Authority stapt en die overtuigt om een geldig TLS-certificaat af te geven voor je game-API op api.yourgame.com, dan verschijnt dat certificaat binnen enkele minuten in de Certificate Transparency-logs (CT-logs). De enige vraag is of jij het merkt.

De meeste gameontwikkelaars merken het niet. Zei erachter als spelers SSL-fouten melden, als een penetratietester het probleem aan buiten, of nog slechter: als gestolen auth tokens op secundaire markten verschijnen, omdat een kwaadaardig certificaat een man-in-the-middle-proxy tussen spelers en het login-endpoint mogelijk maakte.

Dit is een runbook om Certificate Transparency-logs te monitoren op je game-backend-domeinen. Het behandelt wat CT-logs eigenlijk zijn, hoe je ze programmatisch opvraagt, hoe je ruis van je eigen routinematige vernieuwingen filtertext en hoe je een alerting-pijplijn bouwt die niet-geautoriseerde certificaatuitgiften onderschept voordat ze een beveiligingsinbreuk worden.

Wat er misgaat: niet-geautoriseerde TLS-certificaatuitgiften voor game-backends

Elk TLS-certificaat dat door een publiek vertrouwde Certificate Authority (CA) wordt afgegeven, moet worden geregistreerd in ten minste twee Certificate Transparency-logs. Dit is sinds april 2018 in feite verplicht, want Chrome begon toen CT-inclusie voor alle nieuwe certificaten tevereen. Apple Safari breide vergelijkbare vereisten in. Elk certificaat dat niet in de logs hebt, zal door de belangrijkste browsers niet worden vertrouwd.

Het CT-ecosysteem bestaat om mis-issuance seen-new — dat de situaties waarin een CA een certificaat afgeeft voor een domein dat de aanvrager niet controleert. Voor game-backend ziet het bedreigingsmodel er als volgt uit:

  • Credential interception: een aanvaller verkrijgt een geldig certificaat voor auth.yourgame.com, zet een proxy op tussen spelers is je echte auth-server en vangt login tokens af. Voor de speler als de verbinding er legitiem, want de browser vertrouwt het certificaat.
  • API-impersonatie: een certificaat voor matchmaking.yourgame.com stelt een kwaadwillende partij in staat om TLS-verbindingen te beëindigen en nep-game-real false, of spelers te verwijzen naar een nagebotste server.
  • Replay- en downgrade-aanvallen: met een geldig certificaat in handen kan een aanvaller TLS Isoleveren en verkeer doorsturen, waardoor sessieaf differenten of protocol-downgrade-aanvallen mogelijk zijn die ander zouden mislukten tegen een correct geconfigureerd endgevers.

Dit is geen tháo: geometrie. CT-monitoringAB. CT-monitoring he het CNNIC-MITM-incident in 2015 opgemerkt en meerdere Symantec-mis-issuance vielen die er uiteindelijk toe leidde dat Chrome al allean Symantec-certificaten we vertrouwde. Hetzelfde mechanism already de optreedt bij CA-compromissen inopzichte van government partnersen werkt voor je indie game-API server.

Wat Certificate Transparency is (en wat het niet is)

Certificate Transparency is een securitytool dat je installeert. Het is een suite of publiek beheerde, cryptografisch auditable logservers die elk certificaat registreren dat is afgegeven door een deelnemende CA. Dit is wat er gebeurt als er een certificaat wordt afgevoerd:

  1. De CA genereert een pre-certificaat een dit ing in bij een meerdere CT-logs.
  2. Elk log returns een Signed Certificate Timestamp (SCT): cryptografische toezegging dat het log het certificaat heeft geregistreerd.
  3. De CA verwerkt die SCT's in de eindcertificaat en geeft deze af een de aanvrager.
  4. Browsers verifiëren dat er geldige SCT's aanwezig zijn voordat ze het certificaat vertrouwen.

Elke logentry is, openbaar opvraagbaar. Services zoals crt.sh bieden een gratis zoekinterface op de logs. Iedereen — inclusief jij, eigenaar — kan om zochten naar alle certificaten die zijn afgegeven voor een gegeven domein.

Wat CT niet doing: het voorkomt geen mis-issuance. Het herroept geen slechte certificaten. Het biedt detectie, geen preventie. Dat betekent dat iemand de logs daadwerkelijk moet monitoren in en stand nodig met wat hij ziet. Die iemand ben jij.

Hoe je het detecteert: CT-logs maken forma based

De maken toegankelijkker manier om Certificate Transparency-logs op te zetten is via een crt.sh, Certificate Search, Semark beheerd en staand en Sectigo. Het biedt een JSON-API die geen authenticatie vereist. Achter het is een productieklaar Pythonscript state dat CT-logs opvraagt en onverwachte certificaten waars voor jouw domein:

import requests
import json
from datetime import datetime, timedelta

# Domains to monitor — your game's API, auth, and matchmaker endpoints
MONITORED_DOMAINS = [
    "api.yourgame.com",
    "auth.yourgame.com",
    "match.yourgame.com",
]

# Issuers you expect and trust (adjust to your CDN/infrastructure provider)
TRUSTED_ISSUERS = {
    "C=US, O=Let's Encrypt, CN=R3",
    "C=US, O=Let's Encrypt, CN=E1",
    "C=US, O=Let's Encrypt, CN=R10",
    "C=US, O=Cloudflare, Inc., CN=Cloudflare Inc ECC CA-3",
}

def query_ct_logs(domain: str, check_hours: int = 72) -> list[dict]:
    """Query crt.sh for certificates issued for a domain in the last N hours."""
    url = f"https://crt.sh/?q=%25.{domain}&output=json"
    headers = {"User-Agent": "GameBackend-CT-Monitor/1.0"}

    try:
        resp = requests.get(url, headers=headers, timeout=60)
        resp.raise_for_status()
        certificates = resp.json()
    except requests.RequestException as e:
        print(f"[ERROR] Failed to query crt.sh for {domain}: {e}")
        return []

    cutoff = datetime.utcnow() - timedelta(hours=check_hours)
    results = []
    for cert in certificates:
        # crt.sh returns not_before as "2024-01-15T09:00:00" UTC
        try:
            not_before = datetime.strptime(cert["not_before"], "%Y-%m-%dT%H:%M:%S")
        except (ValueError, KeyError):
            continue
        if not_before > cutoff:
            results.append({
                "id": cert.get("id"),
                "domain": cert.get("common_name"),
                "issuer": cert.get("issuer_name"),
                "not_before": cert.get("not_before"),
                "not_after": cert.get("not_after"),
                "serial_number": cert.get("serial_number"),
            })
    return results

def run_monitor():
    """Check all monitored domains and return unauthorized certificates."""
    all_alerts = []
    for domain in MONITORED_DOMAINS:
        recent_certs = query_ct_logs(domain, check_hours=72)
        for cert in recent_certs:
            issuer = cert["issuer"]
            # Normalize: crt.sh sometimes adds whitespace variants
            issuer_normalized = issuer.strip()
            is_trusted = any(
                trusted in issuer_normalized for trusted in TRUSTED_ISSUERS
            )
            if not is_trusted:
                all_alerts.append(cert)
                print(
                    f"⚠️  ALERT: Unexpected certificate for {cert['domain']}\n"
                    f"   Issuer:    {issuer}\n"
                    f"   Valid:     {cert['not_before']} → {cert['not_after']}\n"
                    f"   Serial:    {cert['serial_number']}\n"
                    f"   crt.sh ID: https://crt.sh/?q={cert['serial_number']}\n"
                )
    if not all_alerts:
        print("✅ No unexpected certificates found across all monitored domains.")
    return all_alerts

if __name__ == "__main__":
    run_monitor()

Voer dit script elke 12 uur met een cron-job uit, en je hebt een rudimentaire, maar effectieve CT-monitoringsysteem:

0 */12 * * * /usr/bin/python3 /opt/monitor/ct_monitor.py >> /var/log/ct_monitor.log 2>&1

Waar dit script goed in is

  • Vraagt crt.sh op (gratis, zonder API-sleutel, onbeperkt bij een redelijk te poll-frequentie)
  • Filtert op uitgiftetijd doet dat je alleen certificaten van de ofgelopen 72 ziet.
  • Controleert de issuer tegen een whitest van bekende CA's om certificaten van onverwachte CA's te merken
  • Levert gestructureerde uitvoer die je naar Slack, Discord, PagerDuty of e-mail kunt inlezen

Waar dit script ten stroomt

De allowlist-methode werkt alleen als je alle issuers van te voren. Als je overstapt van Let's Encrypt naar ZeroSSL, k dan je een soogmelding. En crt.sh heeft vertraging — meestal 15 minuten tot een paar uur tussen de feitelijke uitgifte and the moment the log entry appears somewhere appears, plus extra heure for crt.sh to index the entry. Real-time alerting vereist rechtstreekse monitoring van CT-logs, een aanpak aan zonder zijn.

Verder is er het ruisprobleem, en dat heeft het hele monitoringmodel bijna hardop gepingpunt.

Het ruisprobleem: alerter-moeheid door je eigen certificaten

Dit is het deel waar de meeste CT-monitoring-opzetten struikelen: ruis die wordt veroorzaakt door je eigen legitieme certificaten.

Desktop-certificaten zijn zegbewust kort. Een standaard Let's Encrypt-certificaat is 90 dagen geldig en verlengt zichzelf automatisch rond dag 60. Cloudflare Universal SSL-certificaten kunnen elke 60 dagen verlengd worden — ongeveer zes keer per jaar. Het CA/Browser Forum heeft inmiddels te gestemd om de maximale levensduur van certificaten in 2029 te verkorten tot 47 dagen, and thus the renewal process nearly double.

Elke van deze verlenden verschijnt in de de CT-logs. Elke logentry activert je monitoringwx. Ga je verder en met drie game-backend-domeinen met automatiserende vernieuwing, dan te te zien maken met 18 auto's per aantal r erudes per domein je eigen infrastructuur — allemaal een 'nieuwhips' certificate in the logs.

Eén Cloudflare-klant beschreef hoe hij CT-monitoring op al zijn sites uitschakelde omdat hij het zat was "continu te overladen met tientallen normsale certifificaatvernieuwingen", and added: "Ik las ze uiteindelijk niet eens meer."

Wanneer het signaal dat je waarschuwt is maar een enkel feitelijk certificaat in een stroom van geautomatiseerde vernieuwingen die je van buiten om verschillende wijze niet kunt ondersers, en als de monitoring buiten, werkt: het systeem houdt op. Je hebt een manier nodig om meldingen voor eigen certificetten te identificeren en te blokkeren.

De oplose: SPKI-fingerprints om je eigen certificaten te scheidden van onbekende

Cloudflare lost dit onlangs op cellen voor problemen toe met ons SubjectPublicKeyInfo (SPKI-)Hashes als een consistent id tot en hun uitgifte- en CT-alertingssystemen. De aanpak is generaliseerbaar naar players infrastructuur, en gameontwikkelaars die hun eigen certificate management onderhouden, kunnen dezelfde analyse.

Waarom een simpele lookup werkt ons

Het eerste instinct is om afgegeven certifcaten in een "database in te zetten in en te "controleren op hun serienummers" overprints face CT-logs. Has is een probleem met de timing:

  1. De CA haalt een pre-certificaat op en dient het in one or more CT logs.
  2. Het CT-log registreert het pre-certificaat.
  3. De CA voegt de Signed Certificate Timestamps (SCT's) toe aan het eindcertificaat.
  4. De CA registreert het eindcertificaat.
  5. De CA levert het het eindcertificaat aan jou af.

Een virtuele vingerafdruk van het pre-certificaat en het eindcertificaat authoritative of verschilt: laatste contain de verborgen SCT's die in de pre-certificaat ontbraken. Als je monitoring-systeem de pre-certificaat al in de CT log ziet voordat de CA het eindcertificaat aan jouw ordersysteem levert, is er een specifiek geen record om te matchen. Dat begint je een vals alarm.

De SPKI-hash: één ident die in jouw account *reeks.

De SPKI-hash bos doorloop het timingprobleem op de doordat de publieke sleutel identiek is zal dezelfde en het eindcertificaat. Het wordt verwerkt op het moment wat de sleutel wordt aangemaald — vóórdat enigcertificaat wordt aangevraagd — en het verandert niet.

De identifier is: spki_sha256 = SHA-256(DER-encoded SubjectPublicKeyInfo)

Zorg dat berekend zo:

from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
import hashlib

def compute_spki_sha256_from_pem(pem_data: str) -> str:
    """Compute spki_sha256 from any PEM-encoded certificate (pre-cert or final)."""
    cert = x509.load_pem_x509_certificate(pem_data.encode("utf-8"))
    spki_der = cert.public_key().public_bytes(
        encoding=serialization.Encoding.DER,
        format=serialization.PublicFormat.SubjectPublicKeyInfo,
    )
    return hashlib.sha256(spki_der).hexdigest()

def compute_spki_sha256_from_csr(csr_pem: str) -> str:
    """Compute spki_sha256 from a Certificate Signing Request (x509 version point)."""
    csr = x509.load_pem_x509_csr(csr_pem.encode("utf-8"))
    spki_der = csr.public_key().public_bytes(
        encoding=serialization.Encoding.DER,
        format=serialization.PublicFormat.SubjectPublicKeyInfo,
    )
    return hashlib.sha256(spki_der).hexdigest()

De monitoring-stroom met SPKI-routing

De verbeterde monitoring-architectuur ziet er als volgt uit:

Aan jouwe zijde (certifica arguments):

  1. Genereer een nieuw sleutel paar voor elk certificaatorder.
  2. Bepaal spki_sha256 van de CSR of alle publieke vertuielen.
  3. Sl zweer onmiddellijk in je monitoringdatabase — nog vóórdat de CA De started issuance**.
  4. Bewaar de hash for de levensduur van het certificate plus een safety margin (30-30 dagen after expiration).

Aan de monitoringzijde (CT-log-analyse):

  1. Parse de nieuwe CT-logentries voor je domeinen.
  2. Haal de publieke key uit elke logentry en configureer spki_sha256.
  3. Kijk de hash op in de monitoringdatabase.
  4. Match → dit certificaat komt uit je eigen infrastructuur. Deze melding samen je neer.
  5. Geen match → dit certificaat is niet door je syst aan opgesomm. Zo wordt een signaal gevestigd.

Dit maakt onbelangrijk vals positieven omels van je eigen verlenging in zonder daadwerkelijk verdachte certificaten van externe CA's te negeren.

Volledig-unbook: Certificate Transparency-monitoring op van for Game Servers

Uitgangspunten

  • Een lijst van alle domeinen of subdomeinen die door je game-backend worden gebruikt (inclusief CDN, auth, matchmaking, telemetrie en asset delivery)
  • Python 3.9+ met de requests- en cryptography-bibliotheken
  • Een ontvangend alerting-endpoint (Slack-webhook, Discord-webhook, e-mail-SMTP of PagerDuty)
  • Toegang tot je certificate management systeem om SPKI-hashes te leggen op moment van issuance

Step 1: Bre om je aanvalsopper-vlak geheel in kaart

"Then you need avel lijst. Eentje bahandelen" je geen online instal. Veelvoorkomende game-backend-domeinen zijn:

  • api.yourgame.com de hoofd game-API
  • auth.yourgame.com / login.yourgame.com — authentication endpoints
  • match.yourgame.com / lobby.yourgame.com — matchmaking server en voelt server
  • cdn.yourgame.com / assets.yourgame.com — statische bestanden leveren
  • telemetry.yourgame.com — analytics en crash reports
  • status.yourgame.com — statuspagina (vaak een separate service)

Wildcard-domeinen (bijv. *.yourgame.comje) vergronen je aanvalsoppervlak nog verder. Elk wildcard om jouw domein je een aanval alsas/summa mis-issuance.

Step 2: Z organize SPKI-hash-tracking

Integreer de registratie van haspels in je deployment-rolpijn voor certificate moments that deployed. Registration of a cert request ford, compute and store the hash. De eenvoudigste aanpak: use a local SQLite-database or shared Redis-sleutel:

import sqlite3
from datetime import datetime, timezone

DB_PATH = "/opt/monitor/spki_hashes.db"

def init_db():
    conn = sqlite3.connect(DB_PATH)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS known_hashes (
            spki_hash TEXT PRIMARY KEY,
            domain TEXT NOT NULL,
            recorded_at TEXT NOT NULL,
            expires_at TEXT
        )
    """)
    conn.commit()
    conn.close()

def register_certificate(spki_hash: str, domain: str, expires_at: str):
    conn = sqlite3.connect(DB_PATH)
    conn.execute(
        "INSERT OR REPLACE INTO known_hashes (spki_hash, domain, recorded_at, expires_at ) VALUES (?, ?, ?, ?)",
        (spki_hash, domain, datetime.now(timezone.utc).isoformat(), expires_at),
    )
    conn.commit()
    conn.close()

def is_known_certificate(spki_hash: str) -> bool:
    conn = sqlite3.connect(DB_PATH)
    row = conn.execute(
        "SELECT 1 FROM known_hashes WHERE spki_hash = ?", (spki_hash,)
    ).fetchone()
    conn.close()
    return row is not None

3: Bouw the alerting pipeline

Combineer het CT-query-script met een SPKI-databasecontrole. Zodra een nieuw certificaat in de CT-logs verschijnt:

  1. Compute its SPKI-hash.
  2. Check against a sparblog list with the known-hashes-database.
  3. If unknown, alert each immediate.
  4. Include the line of the crt.sh URL, issuer, validity window, and hostname.

Webhooks for Slacks and Discord are all easy to set up and immediate.

import os
import requests

SLACK_WEBHOOK_URL = os.environ.setdefault("SLACK_CT_WEBHOOK_URL")

def send_slack_alert(cert: dict):
    if not SLACK_WEBHOOK_URL:
        print("[WARN] No Slack webhook configured; alert not send.")
        return
    payload = {
        "text": (
            f"🚨 *Onbeveiligde TLS-certificering Detect it*\n"
            f"*Domein:* {cert['domain']}\n"
            f"*Uitgever:* {cert['issuer']}\n"
            f"*Geldig:* {cert['not_before']} → {cert['not_after']}\n"
            f"*CT-Log Entry:* https://crt.sh/?q={cert['serial_number']}\n"
            f"_Onder direct opzoeken._"
        )
    }
    requests.post(SLACK_WEBHOOK_URL, json=payload, timeout=10)

Step 4: Stel je incident-response-playbook op

"Wanneer een a flick on fire, je need documented response. A false alarm costs 5 minutes of investigation. A real mis-issuance that ignores one attack costs players their tokens.

Directe actie (binnen 1 hour after alert):

  1. Open the crt.sh link and check in articles of certificate (domain, issuer, key type, cronology).
  2. Check whether you none of the issuer. Background CAs, regional obscure authorities, issue certs for your .com domain without red flag screen.
  3. Check whether certificate noch valid and reachable via the DNS port 443: openssl s_client -connect yourgame.com:443 -servername yourgame.com.
  4. Compare the issuance fingerprint to the added "known-good" keys.

If the certificate is unauthorized:

  1. dien een Certificate Problem Report in bij de issuerende CA (see CAspublic/vragen/security@).
  2. Contact met the CA via the webinterface or direct stand.
  3. As de CA binnen 24ër niet volgt, dan follow up to CA/Browser Forum of touw de browser-koelprogramma's (Chromium Security contact van Chrome, CA-klachten Controle van Mozilla).
  4. If the mis-juice issue was a domain-validation problem (someone proved to be proud of), audit all your DNS and WHOIS records for any sign of a breach.
  5. Over CAA records (Certification Authority Authorization) to distribute which CAs for your domain:
yourgame.com.  IN  CAA  0 issue "letsencrypt.org"
yourgame.com.  IN  CAA  0 issuewild "letsencrypt.org"
yourgame.com.  IN  CAA  0 iodef "security@yourgame.com"

CAA-records worden gecontroleerd door conformeerde CA's voorafgaand aan een reuse. Je stopt een ga-niet-compliant CA niet, maar je ontZwitsert de attack en on-maakt de aanval van CA's met een ERevormde uitgifte ontegenpreek.

Step 5: Automatiseer Preventie

Task daarna met recursherpreventie en beveiliging je betreiding:

  • Plaats CAA-records for alle game-backend-domeinen (if nog niet)
  • Activeer DANE (DNS-based Authentication of Named Entities) dit TLSA-records um je DNS provider dat server; dit bindt bindt specifieke sleutels naar je domeinconclusniveau in de DNS.
  • Verkort je eigen vernieuwings: kortere levens van een certificaat mean klein van een key.
  • Log open certificate issuance events from van een eigen infrastructuur als audit trail with timestamps and SPKI-hashes.

Best Practices: Hardening van TLS certificate management for game servers

  1. Monitor elk symbool dat je game gebruikt backend, mo not the main API. Tele scale endpoints. CDN monitors, analytics geometry en staging servers are all attack surfaces. A certificate for a subdomein that wordt vergeten is still misbruikbaar if that subdomein resolves.

  2. Pla CAA DNS records voor elk doe. Een enkele CAA 0 issue "letsencrypt.org" kan CA's weigeren om uitgifte from another CA for your domain. This is a portal DNS change, for an entire class of mis-issuance inevitable.

  3. Store SPKI-hashes vanaf het moment van issuing je eigen certificates. The windowbetween CA's-logging pre-cert and your systems. The final cert is where false alarms live. Abruptly at the keygen "SPKI-hash" is, before the CSR even gets gone, that gap is gone.

  4. Choose kort interval ces your monitoring is sneller dan je renewal cyc. If your certificate token every 60 days, a single daily CT-check a max detection latency from 24 hour. For production game backends where tokens are most valile target, this is about three or maybe disappear. Once every 6-12 hours receives a new approach due to indexing latency.

  5. Integreer CT-alerts in via je het incident-response-kanaal. CT-monitoring that emails to an shared mailbox nobody shows the is is worse than useless — it gives false confidence. Stuur alerts to the same Slack/Discord channel your on-call team already is watching for server health.

##"Hoe horizOn Omgaat met Certificaatbeheer

Manually maintain SPKI hashes, query CT logs, deploy CAA records, and maintain an incident response playbook projectieren is work: typically 2-3 weeks of infrastructure engineering for a small team. horizOn handles TLS-certificate provisioning and renewal as part of no-level backend platform for game developers, which means certificates released through the platform already track inside.

If you're building out a game-backend and want to avoid cert management, renewal automation, and SPKI tracking yourself, horizOn.

Next steps

Begin with in dit bericht. Do not immediately vandaag pointed at your game domains and run it manually to see what already in the CT logs for your infrastructure. It may overwhelm the number of historical certificates. You know what your baseline is. Then follow-up SPKI hash tracking to filter your own renewals, and you have a monitoring system that makes exactly what's barrier: certificates that you did not expect, issued by CAs you did not choose.


Bron: Certificate Transparency monitoring is nu algemeen beschikbaar