CORS Proxy untuk Pengambilan Sertifikat
Alat tanda tangan digital menggunakan CORS proxy untuk mengambil sertifikat penerbit dari Certificate Authority (CA) eksternal. Ini diperlukan karena banyak server CA tidak menyertakan header CORS dalam responsnya, sehingga pengambilan langsung dari browser tidak dapat dilakukan.
Selain itu, banyak server CA menyajikan sertifikat melalui HTTP biasa. Saat instans BentoPDF Anda dihosting melalui HTTPS, browser memblokir permintaan HTTP ini (kebijakan mixed content). CORS proxy mengatasi kedua masalah tersebut dengan mengarahkan permintaan melalui endpoint HTTPS dengan header yang tepat.
Cara Kerjanya
Saat menandatangani PDF dengan sertifikat:
- Pustaka
zgapdfsignermencoba membangun rantai sertifikat yang lengkap - Pustaka ini mengambil sertifikat penerbit dari URL yang tertanam di ekstensi AIA (Authority Information Access) sertifikat Anda
- Permintaan ini diarahkan melalui CORS proxy yang menambahkan header
Access-Control-Allow-Originyang diperlukan - Proxy mengembalikan data sertifikat ke browser
Hosting Mandiri CORS Proxy
Jika Anda menghosting BentoPDF sendiri, Anda perlu men-deploy CORS proxy sendiri agar tanda tangan digital dapat bekerja dengan sertifikat yang memerlukan pengambilan rantai.
Opsi 1: Cloudflare Workers (Disarankan)
Instal Wrangler CLI:
bashnpm install -g wranglerLogin ke Cloudflare:
bashwrangler loginClone BentoPDF dan perbarui origin yang diizinkan:
bashgit clone https://github.com/alam00000/bentopdf.git cd bentopdf/cloudflareBuka
cors-proxy-worker.jsdan ubah arrayALLOWED_ORIGINSke domain Anda:jsconst ALLOWED_ORIGINS = [ 'https://your-domain.com', 'https://www.your-domain.com', ];Penting
Tanpa perubahan ini, proxy akan menolak semua permintaan dari situs Anda dengan error 403 Forbidden. Secara default hanya permintaan dari
bentopdf.comyang diizinkan.Deploy proxy:
bashwrangler deployCatat URL worker Anda (mis.,
https://bentopdf-cors-proxy.your-subdomain.workers.dev).Build ulang BentoPDF dengan URL proxy:
Jika menggunakan 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 .Jika build dari sumber:
bashVITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
Opsi 2: Proxy Backend Kustom
Anda juga dapat membuat endpoint proxy sendiri. Persyaratannya:
- Menerima permintaan GET dengan parameter query
url - Mengambil URL tersebut dari server Anda (tanpa pembatasan CORS di sisi server)
- Mengembalikan respons dengan header berikut:
Access-Control-Allow-Origin: https://your-domain.comAccess-Control-Allow-Methods: GET, OPTIONSX-Content-Type-Options: nosniff
Contoh implementasi 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' });
}
});Pertimbangan Keamanan
Cloudflare Worker yang disertakan memiliki beberapa langkah keamanan:
| Fitur | Deskripsi |
|---|---|
| Validasi Origin | Hanya mengizinkan permintaan dari domain yang tercantum di ALLOWED_ORIGINS |
| Pembatasan URL | Hanya mengizinkan URL sertifikat (.crt, .cer, .pem, /certs/, /ocsp, /crl) |
| Pemblokiran IP Privat | Memblokir rentang privat IPv4/IPv6, link-local, loopback, IP desimal, dan metadata cloud |
| Keamanan Content-Type | Hanya mengembalikan MIME type sertifikat yang aman, memblokir injeksi content-type dari upstream |
| Batas Ukuran File | Men-stream respons dengan batas 10MB, menghentikan unduhan jika terlampaui |
| Pembatasan Laju | 60 permintaan per menit per IP atau IPv6 /64 (memerlukan KV, tetap lolos saat KV error) |
| Tanda Tangan HMAC | Penandatanganan sisi klien opsional (mencegah penyalahgunaan biasa) |
DNS rebinding (hosting mandiri di luar Cloudflare)
Worker menolak setiap URL yang me-resolve ke IP privat atau internal. Worker tidak dapat memakai allowlist host tetap, karena URL rantai sertifikat menunjuk ke CA mana pun yang menerbitkan sertifikat (FNMT, CA korporat, dan seterusnya), sehingga tujuannya tidak diketahui sebelumnya. Hal ini menyisakan celah rebinding kecil: seseorang yang menjalankan DNS untuk domainnya sendiri dapat memberikan IP publik pada pemeriksaan keamanan dan IP privat pada permintaan sebenarnya beberapa saat kemudian.
Di Cloudflare hal ini tidak dapat dieksploitasi: Workers tidak dapat menjangkau IP privat atau endpoint metadata 169.254.169.254, sehingga rebind tidak mengenai apa pun. Hanya saja, jangan beri worker jalan masuk: jangan pasang Tunnel, Service, atau binding jaringan privat (worker hanya memerlukan RATE_LIMIT_KV).
Jika Anda menjalankannya di luar Cloudflare (workerd, Node, dan sejenisnya) di dalam VPC, runtime dapat menjangkau IP internal. Pasang firewall egress di depannya dan blokir rentang privat: 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, beserta padanan IPv6-nya. Kode aplikasi tidak dapat menegakkannya setelah Anda berada di luar Cloudflare.
Menonaktifkan Proxy
Jika Anda tidak ingin menggunakan CORS proxy, atur environment variable ke string kosong:
VITE_CORS_PROXY_URL=Catatan: Tanpa proxy, penandatanganan dengan sertifikat yang memerlukan pengambilan rantai eksternal (seperti FNMT atau beberapa CA korporat) akan gagal dengan error "Failed to fetch".
Pemecahan Masalah
"Signing error: TypeError: Failed to fetch"
Ini biasanya berarti salah satu dari:
- CORS proxy belum dikonfigurasi: Atur
VITE_CORS_PROXY_URLdan build ulang - Mixed content diblokir: Situs Anda HTTPS tetapi URL penerbit sertifikat adalah HTTP. CORS proxy mengatasi hal ini.
- CORS proxy menolak origin Anda: Periksa apakah domain Anda ada di array
ALLOWED_ORIGINSpadacors-proxy-worker.js
"403 Forbidden" dari proxy
Domain Anda tidak ada di daftar ALLOWED_ORIGINS. Edit cors-proxy-worker.js:
js
const ALLOWED_ORIGINS = ['https://your-domain.com'];Lalu deploy ulang: npx wrangler deploy
Menguji proxy
bash
curl -H "Origin: https://your-domain.com" \
"https://your-proxy.workers.dev?url=http://www.cert.fnmt.es/certs/ACUSU.crt"Sertifikat yang Berfungsi Tanpa Proxy
Beberapa sertifikat menyertakan rantai lengkap di file P12/PFX dan tidak memerlukan pengambilan eksternal:
- Sertifikat self-signed
- Beberapa CA komersial yang menyertakan sertifikat perantara
- Sertifikat yang Anda rakit sendiri dengan rantai lengkap