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 10 (Zero Errors, No Baseline)
The entire codebase passes PHPStan static analysis at the strictest level (Level 10) 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