PHP Stream Wrappers
debt(d5/e3/b3/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list semgrep and phpstan as the tools, and the code_pattern describes phar:// or data:// in user-controlled paths — these require a SAST rule (semgrep) or static analyser (phpstan) to catch reliably. A default linter would not flag scheme usage in file function arguments, and the misuse is silent at runtime until an attacker triggers it.
Closest to 'simple parameterised fix' (e3). The quick_fix is essentially a targeted call to stream_wrapper_unregister() for dangerous wrappers and whitelisting allowed schemes — a small, localised change. The common_mistakes suggest fixing input validation at the point of file function calls, which is a pattern-level fix rather than a one-liner, but doesn't require touching multiple files architecturally.
Closest to 'localised tax' (b3). The applies_to scope is web and cli contexts broadly, but the actual burden is confined to the layers where user-supplied paths enter file functions. Most of the codebase is unaffected; the tax is paid at validation/input-handling boundaries rather than being load-bearing across the whole system.
Closest to 'serious trap' (t7). The misconception field reveals that developers believe stream wrappers are only used internally by PHP, not realising that custom wrappers can intercept any file operation and that user-controlled paths with phar:// or data:// enable full attack chains. This contradicts the intuitive mental model that file functions simply open files — the 'obvious' assumption that file paths are safe strings is wrong, which is a serious behavioural contradiction from what developers expect.
Also Known As
TL;DR
Explanation
Stream wrappers allow PHP's file functions (fopen, file_get_contents, include) to work over arbitrary protocols. Built-ins include: file:// (local filesystem), http:// / https:// (remote, requires allow_url_fopen=On), php:// (php://input, php://memory, php://temp), phar:// (PHAR archives), and data:// (inline data). Custom wrappers are registered via stream_wrapper_register(). Security: many vulnerabilities exploit unexpected wrapper acceptance — SSRF via http://, PHAR injection via phar://, source disclosure via php://filter. Always validate and restrict the wrappers accepted in user-controlled path arguments.
Watch Out
Common Misconception
Why It Matters
Common Mistakes
- Not restricting allowed stream wrappers when processing user-supplied paths — phar:// and data:// enable attack chains.
- Using php://input for raw request body reading but not limiting the maximum read size.
- Not knowing that php://memory and php://temp are useful for in-memory file operations in tests.
- Allowing user-controlled file paths in file functions without stripping or whitelisting the scheme.
Code Examples
// User-controlled path with unrestricted wrappers:
$path = $_GET['file'];
$content = file_get_contents($path); // phar://, http://, file:// all work
// Attacker: ?file=phar://uploads/img.jpg — deserialization
// Attacker: ?file=php://filter/convert.base64-encode/resource=/etc/passwd
// Built-in wrappers:
file_get_contents('file:///var/www/data.txt'); // local file
file_get_contents('php://input'); // raw POST body
\$h = fopen('php://temp', 'r+'); // temp (mem → disk)
fwrite(\$h, 'data'); rewind(\$h);
echo stream_get_contents(\$h);
// php://filter — transform on read:
\$b64 = file_get_contents('php://filter/convert.base64-encode/resource=file.txt');
// Custom stream wrapper:
class LogStream {
public function stream_open(\$path, \$mode, \$options, &\$opened): bool { return true; }
public function stream_write(string \$data): int {
error_log(\$data); return strlen(\$data);
}
public function stream_eof(): bool { return true; }
public function stream_read(int \$count): string { return ''; }
}
stream_wrapper_register('log', LogStream::class);
file_put_contents('log://channel', 'message sent');