
本文介绍如何在 PHP 中安全检测用户输入的文件名是否包含危险路径(如 ../),从而防止目录遍历攻击;重点讲解 str_contains()(PHP 8+)和 strpos()(兼容低版本)的正确用法及实践注意事项。
本文介绍如何在 php 中安全检测用户输入的文件名是否包含危险路径(如 `../`),从而防止目录遍历攻击;重点讲解 `str_contains()`(php 8+)和 `strpos()`(兼容低版本)的正确用法及实践注意事项。
在 Web 开发中,直接将用户可控参数(如 $_GET['name'])拼接到文件系统操作中极易引发路径遍历(Path Traversal)漏洞。例如,攻击者传入 name=../../etc/passwd,配合 unlink('./uploads/'.$_GET['name']) 可能导致服务器关键文件被误删或读取。PHP 并不存在类似 Python 的 in 操作符用于字符串判断,因此需使用专用字符串函数进行防御。
✅ 正确的检测方式
PHP 8.0 及以上:推荐使用 str_contains()
该函数语义清晰、可读性强,且严格区分大小写(符合路径敏感性要求):
$filename = $_GET['name'] ?? '';
if (str_contains($filename, '../') || str_contains($filename, '..\')) {
http_response_code(403);
die('Error: Permission denied — path traversal attempt detected.');
}
// 安全拼接并执行删除
$filepath = __DIR__ . '/uploads/' . basename($filename);
if (is_file($filepath) && strpos(realpath($filepath), __DIR__ . '/uploads/') === 0) {
unlink($filepath);
} else {
die('Error: Invalid or inaccessible file.');
}PHP
注意必须使用 !== false(而非 == false 或 !strpos(...)),因为 strpos() 在匹配到开头位置(索引 0)时返回 0,而 !0 为 true,会导致逻辑错误:
$filename = $_GET['name'] ?? '';
if (strpos($filename, '../') !== false || strpos($filename, '..\') !== false) {
http_response_code(403);
die('Error: Permission denied — path traversal attempt detected.');
}⚠️ 重要注意事项
- 仅过滤 ../ 不够充分:还需检查 Windows 风格路径 ..、URL 编码绕过(如 %2e%2e%2f)、双重编码、空字节等。生产环境应结合 basename() 提取文件名,并用 realpath() + 白名单路径校验(见上方示例)。
- 避免信任客户端输入:永远不要假设 $_GET 或 $_POST 数据是安全的;所有文件操作前必须做规范化与权限验证。
- 禁用危险字符更彻底:可考虑白名单正则(如 /^[a-zA-Z0-9._-]+$/)限制文件名格式。
- 使用 open_basedir 或沙箱机制:从服务器配置层加固,作为代码防御的补充。
✅ 总结
PHP 中没有 in 字符串操作符,应使用 str_contains()(PHP ≥ 8.0)或 strpos() !== false(兼容旧版)进行子串检测。但单纯的字符串过滤只是基础防线——真正健壮的安全策略需结合路径规范化(realpath)、目录约束校验、最小权限原则及输入白名单等多层防护。切勿因一行“简单过滤”而忽视纵深防御设计。
立即学习“PHP免费学习笔记(深入)”;



















