basename()
debt(d7/e3/b3/t7)
Closest to 'only careful code review or runtime testing' (d7), since semgrep/psalm can flag user-supplied paths in file ops but the misuse (treating basename() as sufficient sanitisation) is semantic and typically caught only in security review.
Closest to 'simple parameterised fix' (e3), per quick_fix: combine basename() with realpath() and whitelist validation — a small pattern replacement at each call site rather than one-line swap.
Closest to 'localised tax' (b3), as basename() usage is typically confined to upload/file-handling components rather than spreading across the system, though applies_to covers web and cli contexts.
Closest to 'serious trap' (t7), per misconception: developers reasonably assume a function named basename() in a security context sanitises the filename, but it only strips directory components and leaves null bytes, locale issues, and other injection vectors — contradicts the safety implied by the name.
Also Known As
TL;DR
Explanation
basename($path) extracts just the filename from a path string, discarding any directory component. This means ../../etc/passwd becomes passwd, preventing directory traversal in simple filename lookups. However, basename() alone is not sufficient protection — it doesn't prevent a user from naming a file to overwrite an existing one, and does not validate the extension or content. Use it as one layer alongside extension allowlists and realpath() checks.
Common Misconception
Why It Matters
Common Mistakes
- Using basename() as a security measure against path traversal — it strips the directory but does not prevent other injection vectors.
- Trusting basename() output as a safe filename for includes — the result still needs whitelist validation.
- Not realising that basename() is locale-dependent for multibyte characters — use the $suffix parameter carefully.
- Combining basename() output with a base directory via string concatenation without realpath() validation after joining.
Code Examples
// basename() alone is not sufficient for safe file access:
$file = basename($_GET['file']);
include '/var/www/templates/' . $file; // Still dangerous without whitelist
// Attacker bypasses with null byte on old PHP: shell.php%00.html
// basename() extracts filename, stripping any path components
basename('/var/www/uploads/image.jpg'); // 'image.jpg'
basename('../../../etc/passwd'); // 'passwd'
basename('photo.jpg', '.jpg'); // 'photo' — strip extension
// Use in file downloads to strip path traversal from user input:
\$safeFile = basename(\$_GET['file'] ?? '');
\$path = '/var/www/uploads/' . \$safeFile;
if (!file_exists(\$path)) abort(404);
readfile(\$path);
// Always combine with realpath() for full safety:
\$base = realpath('/var/www/uploads');
\$path = realpath(\$base . '/' . \$safeFile);
if (!\$path || !str_starts_with(\$path, \$base . DIRECTORY_SEPARATOR)) abort(403);