Zum Inhalt springen

CORS-Proxy zum Abrufen von Zertifikaten ​

Das Werkzeug für digitale Signaturen nutzt einen CORS-Proxy, um Aussteller-Zertifikate von externen Zertifizierungsstellen (CAs) abzurufen. Das ist nötig, weil viele CA-Server keine CORS-Header in ihren Antworten mitsenden, wodurch ein direkter Abruf aus dem Browser verhindert wird.

Außerdem liefern viele CA-Server Zertifikate über einfaches HTTP aus. Wird Ihre BentoPDF-Instanz über HTTPS gehostet, blockieren Browser diese HTTP-Anfragen (Mixed-Content-Richtlinie). Der CORS-Proxy löst beide Probleme, indem er Anfragen über einen HTTPS-Endpunkt mit den passenden Headern leitet.

So funktioniert es ​

Beim Signieren einer PDF mit einem Zertifikat:

  1. Die Bibliothek zgapdfsigner versucht, eine vollständige Zertifikatskette aufzubauen
  2. Sie ruft Aussteller-Zertifikate von URLs ab, die in der AIA-Erweiterung (Authority Information Access) Ihres Zertifikats eingebettet sind
  3. Diese Anfragen laufen über einen CORS-Proxy, der die nötigen Access-Control-Allow-Origin-Header ergänzt
  4. Der Proxy liefert die Zertifikatsdaten an den Browser zurück

Den CORS-Proxy selbst hosten ​

Wenn Sie BentoPDF selbst hosten, müssen Sie einen eigenen CORS-Proxy bereitstellen, damit digitale Signaturen mit Zertifikaten funktionieren, die einen Abruf der Kette erfordern.

Option 1: Cloudflare Workers (empfohlen) ​

  1. Wrangler CLI installieren:

    bash
    npm install -g wrangler
  2. Bei Cloudflare anmelden:

    bash
    wrangler login
  3. BentoPDF klonen und erlaubte Origins anpassen:

    bash
    git clone https://github.com/alam00000/bentopdf.git
    cd bentopdf/cloudflare

    Öffnen Sie cors-proxy-worker.js und änderen Sie das Array ALLOWED_ORIGINS auf Ihre Domain:

    js
    const ALLOWED_ORIGINS = [
      'https://your-domain.com',
      'https://www.your-domain.com',
    ];

    Wichtig

    Ohne diese Änderung lehnt der Proxy alle Anfragen von Ihrer Website mit dem Fehler 403 Forbidden ab. Standardmäßig sind nur Anfragen von bentopdf.com erlaubt.

  4. Den Proxy bereitstellen:

    bash
    wrangler deploy

    Notieren Sie sich die URL Ihres Workers (z. B. https://bentopdf-cors-proxy.your-subdomain.workers.dev).

  5. BentoPDF mit der Proxy-URL neu bauen:

    Bei Verwendung von Docker:

    bash
    export VITE_CORS_PROXY_URL="https://your-worker.workers.dev"
    DOCKER_BUILDKIT=1 docker build \
      --secret id=VITE_CORS_PROXY_URL,env=VITE_CORS_PROXY_URL \
      -t your-bentopdf .

    Beim Bauen aus dem Quellcode:

    bash
    VITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build

Option 2: Eigener Backend-Proxy ​

Sie können auch einen eigenen Proxy-Endpunkt erstellen. Die Anforderungen sind:

  1. GET-Anfragen mit einem Query-Parameter url annehmen
  2. Die URL von Ihrem Server aus abrufen (serverseitig gibt es keine CORS-Beschränkungen)
  3. Die Antwort mit diesen Headern zurückgeben:
    • Access-Control-Allow-Origin: https://your-domain.com
    • Access-Control-Allow-Methods: GET, OPTIONS
    • X-Content-Type-Options: nosniff

Beispielimplementierung mit Express.js:

javascript
app.get('/api/cert-proxy', async (req, res) => {
  const targetUrl = req.query.url;

  // Validate it's a certificate URL
  if (!isValidCertUrl(targetUrl)) {
    return res.status(400).json({ error: 'Invalid URL' });
  }

  try {
    const response = await fetch(targetUrl);
    const data = await response.arrayBuffer();

    res.set('Access-Control-Allow-Origin', 'https://your-domain.com');
    res.set('Content-Type', 'application/octet-stream');
    res.set('X-Content-Type-Options', 'nosniff');
    res.send(Buffer.from(data));
  } catch (error) {
    res.status(500).json({ error: 'Proxy error' });
  }
});

Sicherheitshinweise ​

Der mitgelieferte Cloudflare Worker hat mehrere Sicherheitsmaßnahmen:

FunktionBeschreibung
Origin-PrüfungErlaubt nur Anfragen von Domains aus ALLOWED_ORIGINS
URL-BeschränkungenErlaubt nur Zertifikat-URLs (.crt, .cer, .pem, /certs/, /ocsp, /crl)
Blockierung privater IPsBlockiert private IPv4-/IPv6-Bereiche, Link-Local, Loopback, dezimale IPs und Cloud-Metadaten
Content-Type-SicherheitLiefert nur sicheren Sie Zertifikat-MIME-Typen, blockiert Content-Type-Injection vom Upstream
DateigrößenlimitStreamt die Antwort mit 10MB-Limit und bricht den Download bei Überschreitung ab
Ratenbegrenzung60 Anfragen pro Minute pro IP oder IPv6 /64 (erfordert KV, bei KV-Fehlern wird nicht blockiert)
HMAC-SignaturenOptionale clientseitige Signierung (schreckt gelegentlichen Missbrauch ab)

DNS-Rebinding (Selbst-Hosting außerhalb von Cloudflare) ​

Der Worker lehnt jede URL ab, die auf eine private oder interne IP aufgelöst wird. Eine feste Allowlist von Hosts ist nicht möglich, da Zertifikatsketten-URLs auf die jeweilige CA verweisen, die das Zertifikat ausgestellt hat (FNMT, Unternehmens-CAs usw.), die Ziele also nicht im Voraus bekannt sind. Dadurch bleibt ein kleines Rebinding-Fenster: Wer das DNS für seine eigene Domain betreibt, könnte der Sicherheitsprüfung eine öffentliche IP liefern und der eigentlichen Anfrage einen Moment später eine private.

Auf Cloudflare ist das nicht ausnutzbar: Workers können weder private IPs noch den Metadaten-Endpunkt 169.254.169.254 erreichen, ein Rebind läuft also ins Leere. Geben Sie dem Worker nur keinen Zugang: Hängen Sie keinen Tunnel, Service oder kein Private-Network-Binding an (er braucht nur RATE_LIMIT_KV).

Betreiben Sie ihn außerhalb von Cloudflare (workerd, Node und Ähnliches) in einer VPC, kann die Runtime interne IPs erreichen. Setzen Sie eine Egress-Firewall davor und blockieren Sie die privaten Bereiche: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8, 169.254.0.0/16 sowie die IPv6-Entsprechungen. Anwendungscode kann das außerhalb von Cloudflare nicht durchsetzen.

Den Proxy deaktivieren ​

Wenn Sie keinen CORS-Proxy nutzen möchten, setzen Sie die Umgebungsvariable auf einen leeren String:

VITE_CORS_PROXY_URL=

Hinweis: Ohne den Proxy schlägt das Signieren mit Zertifikaten, die einen externen Abruf der Kette erfordern (wie FNMT oder einige Unternehmens-CAs), mit dem Fehler "Failed to fetch" fehl.

Fehlerbehebung ​

"Signing error: TypeError: Failed to fetch" ​

Das bedeutet meist eines von beidem:

  1. Kein CORS-Proxy konfiguriert: Setzen Sie VITE_CORS_PROXY_URL und bauen Sie neu
  2. Mixed Content blockiert: Ihre Website nutzt HTTPS, aber die Aussteller-URL des Zertifikats ist HTTP. Der CORS-Proxy löst das.
  3. CORS-Proxy lehnt Ihren Origin ab: Prüfen Sie, ob Ihre Domain im Array ALLOWED_ORIGINS in cors-proxy-worker.js steht

"403 Forbidden" vom Proxy ​

Ihre Domain steht nicht in der Liste ALLOWED_ORIGINS. Bearbeiten Sie cors-proxy-worker.js:

js
const ALLOWED_ORIGINS = ['https://your-domain.com'];

Stellen Sie danach erneut bereit: npx wrangler deploy

Den Proxy testen ​

bash
curl -H "Origin: https://your-domain.com" \
  "https://your-proxy.workers.dev?url=http://www.cert.fnmt.es/certs/ACUSU.crt"

Zertifikate, die ohne Proxy funktionieren ​

Manche Zertifikate enthalten die vollständige Kette in der P12/PFX-Datei und benötigen keinen externen Abruf:

  • Selbstsignierte Zertifikate
  • Einige kommerzielle CAs, die Zwischenzertifikate mitliefern
  • Zertifikate, die Sie manuell mit der vollständigen Kette zusammengestellt haben

Doppelt lizenziert unter AGPL-3.0 und kommerzieller Lizenz.