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:
- Die Bibliothek
zgapdfsignerversucht, eine vollständige Zertifikatskette aufzubauen - Sie ruft Aussteller-Zertifikate von URLs ab, die in der AIA-Erweiterung (Authority Information Access) Ihres Zertifikats eingebettet sind
- Diese Anfragen laufen über einen CORS-Proxy, der die nötigen
Access-Control-Allow-Origin-Header ergänzt - 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)
Wrangler CLI installieren:
bashnpm install -g wranglerBei Cloudflare anmelden:
bashwrangler loginBentoPDF klonen und erlaubte Origins anpassen:
bashgit clone https://github.com/alam00000/bentopdf.git cd bentopdf/cloudflareÖffnen Sie
cors-proxy-worker.jsund änderen Sie das ArrayALLOWED_ORIGINSauf Ihre Domain:jsconst 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.comerlaubt.Den Proxy bereitstellen:
bashwrangler deployNotieren Sie sich die URL Ihres Workers (z. B.
https://bentopdf-cors-proxy.your-subdomain.workers.dev).BentoPDF mit der Proxy-URL neu bauen:
Bei Verwendung von Docker:
bashexport 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:
bashVITE_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:
- GET-Anfragen mit einem Query-Parameter
urlannehmen - Die URL von Ihrem Server aus abrufen (serverseitig gibt es keine CORS-Beschränkungen)
- Die Antwort mit diesen Headern zurückgeben:
Access-Control-Allow-Origin: https://your-domain.comAccess-Control-Allow-Methods: GET, OPTIONSX-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:
| Funktion | Beschreibung |
|---|---|
| Origin-Prüfung | Erlaubt nur Anfragen von Domains aus ALLOWED_ORIGINS |
| URL-Beschränkungen | Erlaubt nur Zertifikat-URLs (.crt, .cer, .pem, /certs/, /ocsp, /crl) |
| Blockierung privater IPs | Blockiert private IPv4-/IPv6-Bereiche, Link-Local, Loopback, dezimale IPs und Cloud-Metadaten |
| Content-Type-Sicherheit | Liefert nur sicheren Sie Zertifikat-MIME-Typen, blockiert Content-Type-Injection vom Upstream |
| Dateigrößenlimit | Streamt die Antwort mit 10MB-Limit und bricht den Download bei Überschreitung ab |
| Ratenbegrenzung | 60 Anfragen pro Minute pro IP oder IPv6 /64 (erfordert KV, bei KV-Fehlern wird nicht blockiert) |
| HMAC-Signaturen | Optionale 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:
- Kein CORS-Proxy konfiguriert: Setzen Sie
VITE_CORS_PROXY_URLund bauen Sie neu - Mixed Content blockiert: Ihre Website nutzt HTTPS, aber die Aussteller-URL des Zertifikats ist HTTP. Der CORS-Proxy löst das.
- CORS-Proxy lehnt Ihren Origin ab: Prüfen Sie, ob Ihre Domain im Array
ALLOWED_ORIGINSincors-proxy-worker.jssteht
"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