Security Overview
TCPDF-Next is built on a security-first design philosophy. Every component, from cryptographic primitives to HTML parsing, is engineered to eliminate entire categories of vulnerabilities rather than patch them after discovery.
Security-First Design Philosophy
Security in TCPDF-Next is not a feature bolted on top of a legacy codebase. It is an architectural constraint that influenced every design decision from day one:
- Deny by default — External resource loading, network requests, and file access are blocked unless explicitly allowed.
- Fail closed — When a security check cannot be performed (e.g., OCSP responder unreachable), the operation fails rather than proceeding insecurely.
- Defense in depth — Multiple independent layers of protection ensure that a single bypass does not compromise the system.
- Minimal attack surface — Zero runtime Composer dependencies. All cryptographic operations use PHP's built-in OpenSSL and Sodium extensions.
AES-256 Encryption (No Legacy Algorithms)
TCPDF-Next exclusively implements AES-256 encryption as defined in PDF 2.0 (ISO 32000-2, Revision 6). All legacy and insecure algorithms are permanently rejected:
| Algorithm | Status | Reason |
|---|---|---|
| AES-256-CBC | Supported | PDF 2.0 standard, no known practical attacks |
| RC4 (40-bit / 128-bit) | Prohibited | Stream cipher with known biases and practical attacks |
| AES-128 | Prohibited | Insufficient margin for long-term confidentiality |
| DES / 3DES | Not implemented | Block size and key length vulnerabilities |
| MD5 (for key derivation) | Prohibited | Collision attacks since 2004 |
use YeeeFang\TcpdfNext\Encryption\EncryptionAlgorithm;
use YeeeFang\TcpdfNext\Encryption\Permissions;
$pdf->setEncryption()
->setAlgorithm(EncryptionAlgorithm::AES256)
->setUserPassword('reader-password')
->setOwnerPassword('admin-password')
->setPermissions(
Permissions::PRINT_HIGH_QUALITY
| Permissions::COPY
| Permissions::ACCESSIBILITY
)
->apply();PAdES Digital Signatures (B-B through B-LTA)
TCPDF-Next implements the full PAdES Baseline Profile (ETSI EN 319 142-1) for digital signatures with increasing levels of long-term validity:
| Level | Description | Validation Period |
|---|---|---|
| PAdES B-B | Basic CMS signature with signing certificate | Certificate validity (~1-3 years) |
| PAdES B-T | + RFC 3161 timestamp from a trusted TSA | TSA certificate validity (~10-15 years) |
| PAdES B-LT | + Document Security Store with OCSP/CRL data | Algorithm security lifetime (~15-30 years) |
| PAdES B-LTA | + Archive timestamp for indefinite re-validation | Indefinite (with periodic re-timestamping) |
For implementation details, see PAdES B-LTA Signatures.
SSRF Protection with DNS Pinning
All external network requests (image fetching, TSA communication, OCSP lookups) pass through a hardened HTTP client with built-in SSRF protection:
- DNS pinning — Resolved IP addresses are validated before connection. Private network ranges (
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16), loopback (127.0.0.0/8), and link-local (169.254.0.0/16) addresses are blocked. - Protocol restriction — Only
https://is permitted by default. Plainhttp://is rejected unless explicitly allowed. - Domain allowlisting — Configurable allowlist for permitted external domains.
- Redirect following — Limited to a configurable maximum (default: 3) with re-validation at each hop.
Path Traversal Prevention
All file path operations are sanitized to prevent directory traversal attacks:
- Embedded file names are stripped of path separators and
..sequences. - Font file paths are resolved to absolute canonical paths and validated against allowed directories.
- Image paths referenced in HTML are restricted to explicitly configured directories via
ResourcePolicy.
#[\SensitiveParameter] on Passwords and Keys
All method parameters that accept passwords, passphrases, private keys, or PINs are annotated with PHP 8.2's #[\SensitiveParameter] attribute. This ensures that sensitive values are automatically redacted from stack traces, error logs, and exception messages:
public function setUserPassword(
#[\SensitiveParameter] string $password
): self { /* ... */ }
public function setCertificate(
string $certificate,
#[\SensitiveParameter] string $privateKey,
#[\SensitiveParameter] string $passphrase = ''
): self { /* ... */ }#[\NoDiscard] on Critical Return Values
Methods that return security-critical results (validation outcomes, signature verification) are annotated with #[\NoDiscard] to prevent callers from ignoring return values:
#[\NoDiscard]
public function validate(string $pdfPath): ValidationResult { /* ... */ }
#[\NoDiscard]
public function verify(): SignatureVerificationResult { /* ... */ }Ignoring these return values produces a compiler warning, catching a common class of security bugs at development time.
PHPStan Level 8 (Zero Errors, No Baseline)
The entire codebase passes PHPStan static analysis at the strictest level (level 8) with zero errors and no baseline file. This means:
- No
@phpstan-ignoreannotations anywhere in the codebase. - No suppressed error categories.
- All types are fully specified, including generics and template types.
- All dead code paths are eliminated.
100% declare(strict_types=1)
Every PHP file in TCPDF-Next begins with declare(strict_types=1). There are no exceptions. This eliminates an entire class of type coercion bugs that have historically led to security vulnerabilities in PHP applications.
OWASP Compliance Considerations
TCPDF-Next addresses the following OWASP categories relevant to PDF generation libraries:
| OWASP Category | Mitigation |
|---|---|
| A01 — Broken Access Control | Granular PDF permissions, certificate-based encryption |
| A02 — Cryptographic Failures | AES-256 only, no weak algorithms, constant-time comparisons |
| A03 — Injection | HTML sanitization, path traversal prevention, no eval() |
| A05 — Security Misconfiguration | Secure defaults, deny-by-default resource policies |
| A06 — Vulnerable Components | Zero runtime dependencies, built-in crypto via OpenSSL/Sodium |
| A07 — Authentication Failures | #[\SensitiveParameter], secure memory wiping via sodium_memzero() |
| A10 — SSRF | DNS pinning, private network blocking, domain allowlisting |
Security Documentation
Explore the full security documentation:
- Security Best Practices — Input validation, certificate management, deployment security
- Security Overview — PDF signature attack vectors and mitigations