CORS-прокси для получения сертификатов
Инструмент цифровой подписи использует CORS-прокси для получения сертификатов издателей у внешних удостоверяющих центров (CA). Это нужно, потому что многие серверы CA не добавляют заголовки CORS в ответы, и браузер не может запросить сертификаты напрямую.
Кроме того, многие серверы CA отдают сертификаты по обычному HTTP. Если ваш экземпляр BentoPDF работает по HTTPS, браузеры блокируют такие HTTP-запросы (политика смешанного содержимого). CORS-прокси решает обе проблемы: запросы идут через HTTPS-endpoint с нужными заголовками.
Как это работает
При подписании PDF сертификатом:
- Библиотека
zgapdfsignerпытается построить полную цепочку сертификатов - Она запрашивает сертификаты издателей по URL из расширения AIA (Authority Information Access) вашего сертификата
- Эти запросы идут через CORS-прокси, который добавляет нужные заголовки
Access-Control-Allow-Origin - Прокси возвращает данные сертификата в браузер
Собственный CORS-прокси
Если вы размещаете BentoPDF у себя, для работы цифровых подписей с сертификатами, которым нужна загрузка цепочки, потребуется развернуть собственный CORS-прокси.
Вариант 1: Cloudflare Workers (рекомендуется)
Установите Wrangler CLI:
bashnpm install -g wranglerВойдите в Cloudflare:
bashwrangler loginКлонируйте BentoPDF и обновите разрешённые источники:
bashgit clone https://github.com/alam00000/bentopdf.git cd bentopdf/cloudflareОткройте
cors-proxy-worker.jsи замените массивALLOWED_ORIGINSна свой домен:jsconst ALLOWED_ORIGINS = [ 'https://your-domain.com', 'https://www.your-domain.com', ];Важно
Без этого изменения прокси отклонит все запросы с вашего сайта с ошибкой 403 Forbidden. По умолчанию разрешены только запросы с
bentopdf.com.Разверните прокси:
bashwrangler deployЗапомните URL своего worker (например,
https://bentopdf-cors-proxy.your-subdomain.workers.dev).Пересоберите BentoPDF с URL прокси:
Если вы используете 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 .Если вы собираете из исходников:
bashVITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
Вариант 2: собственный серверный прокси
Можно создать и собственный endpoint прокси. Требования:
- Принимать GET-запросы с параметром запроса
url - Запрашивать этот URL со своего сервера (на стороне сервера ограничений CORS нет)
- Возвращать ответ с такими заголовками:
Access-Control-Allow-Origin: https://your-domain.comAccess-Control-Allow-Methods: GET, OPTIONSX-Content-Type-Options: nosniff
Пример реализации на 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' });
}
});Вопросы безопасности
В поставляемом Cloudflare Worker предусмотрено несколько мер безопасности:
| Функция | Описание |
|---|---|
| Проверка источника | Разрешает запросы только с доменов из ALLOWED_ORIGINS |
| Ограничение URL | Разрешает только URL сертификатов (.crt, .cer, .pem, /certs/, /ocsp, /crl) |
| Блокировка частных IP | Блокирует частные диапазоны IPv4/IPv6, link-local, loopback, десятичные IP и облачные метаданные |
| Безопасный Content-Type | Возвращает только безопасные MIME-типы сертификатов, блокирует подмену content-type источником |
| Лимит размера файла | Передаёт ответ потоком с лимитом 10 МБ и прерывает загрузку при превышении |
| Ограничение частоты | 60 запросов в минуту на IP или IPv6 /64 (нужен KV, при ошибках KV пропускает запросы) |
| Подписи HMAC | Необязательная подпись на стороне клиента (сдерживает случайные злоупотребления) |
DNS rebinding (хостинг вне Cloudflare)
Worker отклоняет любой URL, который разрешается в частный или внутренний IP. Фиксированный список разрешённых хостов использовать нельзя, так как URL цепочек сертификатов указывают на тот CA, который выдал сертификат (FNMT, корпоративные CA и так далее), и адреса заранее неизвестны. Поэтому остаётся небольшое окно для rebinding: тот, кто управляет DNS своего домена, может отдать проверке безопасности публичный IP, а настоящему запросу чуть позже частный.
В Cloudflare это не эксплуатируется: Workers не могут обращаться к частным IP и endpoint метаданных 169.254.169.254, поэтому rebind ни к чему не приведёт. Только не давайте worker доступа внутрь: не подключайте Tunnel, Service или привязку к частной сети (ему нужен лишь RATE_LIMIT_KV).
Если вы запускаете его вне Cloudflare (workerd, Node и подобное) внутри VPC, среда может обращаться к внутренним IP. Поставьте перед ней исходящий файрвол и заблокируйте частные диапазоны: 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 и их аналоги IPv6. Код приложения не может этого обеспечить, когда вы вне Cloudflare.
Отключение прокси
Если CORS-прокси вам не нужен, задайте переменной окружения пустую строку:
VITE_CORS_PROXY_URL=Примечание: без прокси подпись сертификатами, которым нужна внешняя загрузка цепочки (например, FNMT или некоторые корпоративные CA), завершится ошибкой "Failed to fetch".
Устранение неполадок
"Signing error: TypeError: Failed to fetch"
Обычно это означает одно из двух:
- CORS-прокси не настроен: задайте
VITE_CORS_PROXY_URLи пересоберите проект - Смешанное содержимое заблокировано: ваш сайт работает по HTTPS, а URL издателя сертификата по HTTP. CORS-прокси решает эту проблему.
- CORS-прокси отклоняет ваш источник: проверьте, что ваш домен есть в массиве
ALLOWED_ORIGINSвcors-proxy-worker.js
"403 Forbidden" от прокси
Вашего домена нет в списке ALLOWED_ORIGINS. Отредактируйте cors-proxy-worker.js:
js
const ALLOWED_ORIGINS = ['https://your-domain.com'];Затем разверните заново: npx wrangler deploy
Проверка прокси
bash
curl -H "Origin: https://your-domain.com" \
"https://your-proxy.workers.dev?url=http://www.cert.fnmt.es/certs/ACUSU.crt"Сертификаты, работающие без прокси
Некоторые сертификаты содержат полную цепочку в файле P12/PFX и не требуют внешней загрузки:
- Самоподписанные сертификаты
- Некоторые коммерческие CA, которые включают промежуточные сертификаты
- Сертификаты, для которых вы вручную собрали полную цепочку