PHAR Archives & Packaging
debt(d5/e5/b3/t9)
Closest to 'specialist tool catches it' (d5). The detection_hints list semgrep and psalm as tools, both specialist static analysis tools. The pattern — user-controlled paths reaching file functions — is not caught by default linters or the compiler; it requires dedicated SAST rules (semgrep patterns for phar:// wrapper misuse) or Psalm taint analysis.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix sounds simple ('never allow user-controlled input in file operations'), but in practice every file function call site that accepts user-supplied paths must be audited and sanitised or the phar:// stream wrapper disabled globally — this is a cross-cutting sweep across potentially many files rather than a single-line patch.
Closest to 'localised tax' (b3). The PHAR concern applies to web and CLI contexts but the burden is largely isolated: developers writing file-handling code must stay aware of stream wrapper risks, but most of the codebase that doesn't touch file operations is unaffected. The ongoing tax is real but contained to file-handling code paths.
Closest to 'catastrophic trap — the obvious way is always wrong' (t9). The misconception field captures it precisely: developers believe PHAR is only relevant when intentionally distributing archives. In reality, any file function receiving a user-controlled path starting with phar:// triggers PHAR deserialization — meaning the attack surface is every file operation in the codebase, not just explicit PHAR usage. The 'obvious' assumption (PHAR = packaging tool, not a security concern for my app) is categorically wrong.
Also Known As
TL;DR
Explanation
A PHAR (PHP Archive) bundles PHP code, assets, and metadata into a single file. The stub bootstraps execution. PHARs are used by Composer (composer.phar), PHPUnit, and many CLI tools — run with php tool.phar, required, or accessed via the phar:// stream wrapper. Creating PHARs requires phar.readonly=0 in php.ini. Critical security note: PHAR metadata is deserialized on any file operation using a phar:// URI — enabling PHAR injection attacks without a direct unserialize() call. Never allow user-controlled input to reach file functions that might accept phar:// paths.
Common Misconception
Why It Matters
Common Mistakes
- Not disabling phar.readonly in php.ini when creating PHARs — required for writing but creates security risk if left on.
- Using user-controlled paths in file functions without blocking phar:// wrapper — triggers PHAR deserialization.
- Including dev dependencies in a distributed PHAR — increases file size and attack surface.
- Not signing PHARs for distributed tools — an unsigned PHAR can be replaced with a malicious one.
Code Examples
// User path in file function — PHAR injection:
$path = $_GET['file'];
if (file_exists($path)) { // ?file=phar://uploads/evil.jpg
include $path; // Deserializes and executes PHAR metadata
}
// Create a distributable CLI tool as a PHAR archive
$phar = new Phar('mytool.phar');
$phar->startBuffering();
$phar->buildFromDirectory(__DIR__ . '/src', '/\.php\$/');
$phar->setDefaultStub('bin/mytool.php', 'bin/mytool.php');
$phar->stopBuffering();
chmod('mytool.phar', 0755);
// Run: php mytool.phar --help
// Security:
; php.ini: phar.readonly = On — prevents modification of PHAR files
// Never pass attacker-controlled paths to phar:// stream wrapper
// Validate PHAR signatures before extracting untrusted archives