Лучшие практики безопасности
Это руководство предоставляет практические рекомендации по безопасности для развёртывания TCPDF-Next в продакшен-окружениях. Следование этим практикам гарантирует, что ваш конвейер генерации PDF соответствует корпоративным стандартам безопасности.
Валидация ввода (санитизация HTML перед writeHtml)
При генерации PDF из HTML, предоставленного пользователем, всегда санитизируйте ввод перед передачей в HTML-рендерер. HtmlRenderer TCPDF-Next парсит и рендерит HTML точно, что означает, что вредоносная разметка может быть использована, если не санитизирована.
use YeeeFang\TcpdfNext\Html\HtmlRenderer;
// DANGEROUS: Never pass raw user input directly
// $renderer->writeHtml($userInput);
// SAFE: Sanitize first with a dedicated library
$clean = \HTMLPurifier::getInstance()->purify($userInput);
$renderer->writeHtml($clean);Ключевые правила:
- Удалите теги
<script>,<iframe>,<object>,<embed>и<link>перед рендерингом. - Удалите схемы URI
javascript:иdata:из атрибутовhrefиsrc. - Ограничьте разрешённые CSS-свойства теми, которые нужны для макета (без
position: fixed, безurl()в CSS-значениях). - Проверьте кодировку символов — убедитесь, что ввод является валидным UTF-8 перед передачей рендереру.
Управление сертификатами (безопасное хранение и ротация)
Иерархия хранения
| Метод | Уровень безопасности | Случай использования |
|---|---|---|
| Аппаратный модуль безопасности (HSM) | Наивысший | Производственные среды, регулируемые отрасли |
| Облачный KMS (AWS KMS, Azure Key Vault, GCP KMS) | Высокий | Облачные развёртывания |
| PKCS#12-файл с надёжной парольной фразой | Средний | Небольшие развёртывания |
| PEM-файл (зашифрованный) | Средне-низкий | Разработка, тестирование |
| PEM-файл (незашифрованный) | Наинизший | Никогда в продакшене |
Политика ротации
- Обновляйте сертификаты не менее чем за 30 дней до истечения срока.
- Мониторьте истечение с автоматическими оповещениями на 30, 14, 7 и 1 день.
- Немедленно отзывайте скомпрометированные сертификаты через выдающий CA.
- Ведите подписанный журнал аудита всех операций жизненного цикла сертификатов.
use YeeeFang\TcpdfNext\Certificate\CertificateStore;
$store = new CertificateStore();
$store->loadFromDirectory('/etc/tcpdf-next/certs/', '*.pem');
$activeCert = $store->getActiveCertificate('document-signing');
if ($activeCert->getExpirationDate() < new \DateTimeImmutable('+30 days')) {
$logger->warning('Signing certificate expires soon', [
'subject' => $activeCert->getSubject(),
'expires' => $activeCert->getExpirationDate()->format('Y-m-d'),
]);
}DANGER
Никогда не храните приватные ключи в репозиториях исходного кода, незашифрованных файлах на общих файловых системах, столбцах базы данных без шифрования в покое или лог-файлах.
Обработка паролей (SASLprep и надёжные пароли)
При установке паролей шифрования PDF применяйте нормализацию Unicode через SASLprep (RFC 4013) для обеспечения согласованной обработки паролей на разных платформах:
// TCPDF-Next automatically applies SASLprep to passwords
$pdf->setEncryption()
->setAlgorithm(EncryptionAlgorithm::AES256)
->setUserPassword('pässwörd-with-ünïcöde') // SASLprep normalized internally
->setOwnerPassword($strongOwnerPassword)
->apply();Рекомендации по парольной политике:
- Минимум 12 символов для пользовательских паролей, 20 символов для паролей владельца.
- Используйте криптографически безопасный генератор случайных чисел для паролей владельца (
random_bytes()). - Никогда не прописывайте пароли в исходном коде — загружайте из переменных окружения или менеджеров секретов.
- Очищайте пароли из памяти после использования через
sodium_memzero().
Предотвращение SSRF (валидация URL для изображений, TSA, OCSP)
TCPDF-Next блокирует SSRF по умолчанию, но необходимо настроить белые списки для легитимных внешних ресурсов:
use YeeeFang\TcpdfNext\Security\NetworkPolicy;
$networkPolicy = NetworkPolicy::create()
->denyPrivateNetworks() // Block 10.x, 172.16.x, 192.168.x
->denyLoopback() // Block 127.0.0.1
->denyLinkLocal() // Block 169.254.x
->allowDomain('cdn.yourcompany.com') // Images
->allowDomain('timestamp.digicert.com') // TSA
->allowDomain('ocsp.digicert.com') // OCSP
->setMaxRedirects(3)
->setRequestTimeout(10);
$pdf = PdfDocument::create()
->setNetworkPolicy($networkPolicy)
->build();Чек-лист:
- Проверяйте все URL перед загрузкой (схема, хост, порт).
- Явно добавляйте в белый список домены TSA и OCSP-респондеров.
- Блокируйте
file://,gopher://,ftp://и другие не-HTTP(S) схемы. - Логируйте все заблокированные запросы для мониторинга безопасности.
Валидация путей файлов (предотвращение обхода путей)
При приёме путей к файлам от пользователя (например, для файлов шрифтов, изображений или путей вывода):
use YeeeFang\TcpdfNext\Security\ResourcePolicy;
$resourcePolicy = ResourcePolicy::strict()
->allowLocalDirectory('/app/public/assets/')
->allowLocalDirectory('/app/storage/fonts/')
->denyAllRemote();
$pdf = PdfDocument::create()
->setResourcePolicy($resourcePolicy)
->build();Правила:
- Никогда не конкатенируйте пользовательский ввод напрямую в пути к файлам.
- Преобразуйте пути в абсолютную каноническую форму и проверяйте относительно разрешённых каталогов.
- Отклоняйте пути, содержащие
.., нулевые байты или непечатаемые символы. - Используйте
ResourcePolicy::strict()в продакшене — он запрещает весь доступ по умолчанию.
Безопасность развёртывания (Docker и права доступа к файлам)
Конфигурация Docker
FROM php:8.5-fpm-alpine
# Run as non-root user
RUN addgroup -S tcpdf && adduser -S tcpdf -G tcpdf
USER tcpdf
# Disable dangerous PHP functions
RUN echo "disable_functions = exec,passthru,shell_exec,system,proc_open,popen" \
>> /usr/local/etc/php/conf.d/security.ini
# Read-only filesystem (mount writable volumes explicitly)
# docker run --read-only --tmpfs /tmp ...Права доступа к файлам
# Certificate directory: readable only by the web server user
chown -R www-data:www-data /etc/tcpdf-next/certs/
chmod 700 /etc/tcpdf-next/certs/
chmod 600 /etc/tcpdf-next/certs/*.p12
chmod 600 /etc/tcpdf-next/certs/*.pem
# Output directory: writable only by the web server user
chown -R www-data:www-data /var/lib/tcpdf-next/output/
chmod 700 /var/lib/tcpdf-next/output/
# Temporary directory: writable, not world-readable
chown -R www-data:www-data /tmp/tcpdf-next/
chmod 700 /tmp/tcpdf-next/Content Security Policy для PDF, отображаемых в браузере
При доставке PDF inline в браузере установите соответствующие HTTP-заголовки для предотвращения атак встраивания:
return response($pdf->toString(), 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'inline; filename="document.pdf"',
'Content-Security-Policy' => "default-src 'none'; plugin-types application/pdf",
'X-Content-Type-Options' => 'nosniff',
'X-Frame-Options' => 'DENY',
'Cache-Control' => 'no-store, no-cache, must-revalidate',
]);Для PDF, содержащих конфиденциальные данные, предпочитайте Content-Disposition: attachment для принудительной загрузки вместо рендеринга в браузере.
Рекомендации по журналированию аудита
Настройте комплексное журналирование аудита для всех операций PDF, критичных с точки зрения безопасности:
use YeeeFang\TcpdfNext\Security\AuditLogger;
AuditLogger::configure([
'channel' => 'tcpdf-security',
'log_signing' => true,
'log_encryption' => true,
'log_validation' => true,
'log_key_access' => true,
'log_tsa_requests' => true,
'log_resource_access' => true, // Log image/font loading
'log_blocked_requests' => true, // Log SSRF blocks
'redact_sensitive' => true, // Redact passwords/keys from logs
]);Мониторьте:
- Неудачные попытки валидации подписей (возможная подделка документа).
- Предупреждения об истечении срока сертификатов.
- Сбои связи с TSA.
- Необычный объём подписания (возможная компрометация ключа).
- Заблокированные попытки SSRF (возможное зондирование атаки).
- Загрузка ресурсов с неожиданных путей.
Принцип минимальных привилегий для разрешений PDF
При установке разрешений PDF-документа предоставляйте только минимально необходимый доступ:
use YeeeFang\TcpdfNext\Encryption\Permissions;
// RESTRICTIVE: Read-only document
$pdf->setEncryption()
->setPermissions(Permissions::ACCESSIBILITY) // Only screen reader access
->setUserPassword('reader')
->setOwnerPassword($strongOwnerPassword)
->apply();
// MODERATE: Printable document
$pdf->setEncryption()
->setPermissions(
Permissions::PRINT_HIGH_QUALITY
| Permissions::ACCESSIBILITY
)
->apply();
// AVOID: Granting all permissions defeats the purpose of encryption
// Permissions::ALL is available but should rarely be usedРекомендации по разрешениям:
- Никогда не предоставляйте
MODIFY_CONTENTS, если получателю не нужно редактировать документ. - Всегда предоставляйте
ACCESSIBILITYдля совместимости с программами чтения с экрана (юридическое требование во многих юрисдикциях). - Используйте
PRINT_HIGH_QUALITYвместоPRINT_LOW_QUALITY, если нет конкретной причины. - Документируйте обоснование каждого предоставленного разрешения в вашем коде.
Дополнительные материалы
- Обзор безопасности — Архитектура безопасности и философия проектирования
- PAdES B-LTA — Уровни подписей и реализация
- Продвинутое шифрование — Требования к алгоритмам