Files
bentopdf/docs/ru/self-hosting/cors-proxy.md
T

181 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CORS-прокси для получения сертификатов
Инструмент цифровой подписи использует CORS-прокси для получения сертификатов издателей у внешних удостоверяющих центров (CA). Это нужно, потому что многие серверы CA не добавляют заголовки CORS в ответы, и браузер не может запросить сертификаты напрямую.
Кроме того, многие серверы CA отдают сертификаты по обычному HTTP. Если ваш экземпляр BentoPDF работает по HTTPS, браузеры блокируют такие HTTP-запросы (политика смешанного содержимого). CORS-прокси решает обе проблемы: запросы идут через HTTPS-endpoint с нужными заголовками.
## Как это работает
При подписании PDF сертификатом:
1. Библиотека `zgapdfsigner` пытается построить полную цепочку сертификатов
2. Она запрашивает сертификаты издателей по URL из расширения AIA (Authority Information Access) вашего сертификата
3. Эти запросы идут через CORS-прокси, который добавляет нужные заголовки `Access-Control-Allow-Origin`
4. Прокси возвращает данные сертификата в браузер
## Собственный CORS-прокси
Если вы размещаете BentoPDF у себя, для работы цифровых подписей с сертификатами, которым нужна загрузка цепочки, потребуется развернуть собственный CORS-прокси.
### Вариант 1: Cloudflare Workers (рекомендуется)
1. **Установите Wrangler CLI**:
```bash
npm install -g wrangler
```
2. **Войдите в Cloudflare**:
```bash
wrangler login
```
3. **Клонируйте BentoPDF и обновите разрешённые источники**:
```bash
git clone https://github.com/alam00000/bentopdf.git
cd bentopdf/cloudflare
```
Откройте `cors-proxy-worker.js` и замените массив `ALLOWED_ORIGINS` на свой домен:
```js
const ALLOWED_ORIGINS = [
'https://your-domain.com',
'https://www.your-domain.com',
];
```
::: warning Важно
Без этого изменения прокси отклонит все запросы с вашего сайта с ошибкой **403 Forbidden**. По умолчанию разрешены только запросы с `bentopdf.com`.
:::
4. **Разверните прокси**:
```bash
wrangler deploy
```
Запомните URL своего worker (например, `https://bentopdf-cors-proxy.your-subdomain.workers.dev`).
5. **Пересоберите BentoPDF с URL прокси**:
Если вы используете 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 .
```
Если вы собираете из исходников:
```bash
VITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
```
### Вариант 2: собственный серверный прокси
Можно создать и собственный endpoint прокси. Требования:
1. Принимать GET-запросы с параметром запроса `url`
2. Запрашивать этот URL со своего сервера (на стороне сервера ограничений CORS нет)
3. Возвращать ответ с такими заголовками:
- `Access-Control-Allow-Origin: https://your-domain.com`
- `Access-Control-Allow-Methods: GET, OPTIONS`
- `X-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"
Обычно это означает одно из двух:
1. **CORS-прокси не настроен**: задайте `VITE_CORS_PROXY_URL` и пересоберите проект
2. **Смешанное содержимое заблокировано**: ваш сайт работает по HTTPS, а URL издателя сертификата по HTTP. CORS-прокси решает эту проблему.
3. **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, которые включают промежуточные сертификаты
- Сертификаты, для которых вы вручную собрали полную цепочку