file_get_contents本身无漏洞,风险在于未校验的用户输入拼接路径:需先rawurldecode解码,再realpath规范化,最后用strpos严格比对以/结尾的安全根目录前缀。

file_get_contents 本身没有漏洞,问题出在你把用户输入直接拼进路径里还未经校验就传给它——这是典型的路径遍历入口点。
为什么 file_get_contents 容易触发路径遍历
只要参数是拼接出来的、且未做规范化和白名单约束,file_get_contents 就会忠实地读取任意路径。攻击者提交 ../../../etc/passwd 或 URL 编码变体(如 %2e%2e%2f),而 PHP 默认不自动解码 GET 参数,realpath() 又只对已解码路径生效,这就形成绕过缺口。
常见错误写法:
$file = $_GET['file'];
$content = file_get_contents('/var/www/data/' . $file); // 危险!
- 没过滤
..、/、 - 没统一解码(
urldecode或rawurldecode) - 没限制根目录范围,
realpath返回的路径可能跳到 Web 目录外
必须做的三步校验:解码 → 规范化 → 白名单比对
不能只靠 basename 或简单 str_replace,那会被 ....//、%c0%ae%c0%ae%2f 绕过。正确顺序是:
- 先用
rawurldecode解码一次(urldecode对+处理不同,rawurldecode更稳妥) - 再用
realpath获取绝对路径(注意:若路径不存在,realpath返回false) - 最后严格比对是否落在预设安全根目录下,用
strpos($realPath, $safeRoot) === 0,不能用===或==,避免前缀匹配误判
示例:
$safeRoot = '/var/www/data/';
$file = $_GET['file'] ?? '';
$file = rawurldecode($file);
$realPath = realpath('/var/www/data/' . $file);
if ($realPath === false || strpos($realPath, $safeRoot) !== 0) {
http_response_code(403);
exit('Access denied');
}
$content = file_get_contents($realPath);
file_get_contents 的替代方案与风险提示
某些场景下,硬塞路径不如换思路:
- 用 ID 映射文件名:
$map = ['1' => 'report.pdf', '2' => 'log.txt'];,用户只传id=1 - 读取前缀固定的小文件,可改用
file_exists+is_file+basename组合,但依然要检查路径是否越界 - 禁用
allow_url_fopen(尤其线上环境),防止file_get_contents('php://filter/...')或远程协议读取
特别注意:realpath 在 Windows 下对反斜杠处理不稳定,跨平台项目建议统一用 / 分隔符,并在拼接前用 str_replace(['\', '//'], '/', $path) 清理。
最常被忽略的一点:即使加了 realpath,如果 $safeRoot 末尾没带 /,比如写成 '/var/www/data',那么 /var/www/data2/secret.txt 也会通过 strpos 检查——因为它是 /var/www/data2 的前缀。根目录变量必须以 / 结尾,且比对逻辑不能有例外路径。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











