本地文件包含如何防御

wen 网络安全 27

从原理到实战的硬核指南

目录导读

  1. 理解攻击者的视角:LFI漏洞如何被利用
  2. 核心防御策略:输入验证与路径白名单
  3. 文件系统隔离与最小权限原则
  4. WAF与运行时防护的实战配置
  5. 问答环节:常见防御误区与终极方案

理解攻击者的视角:LFI漏洞如何被利用

为什么需要了解攻击者?
本地文件包含(LFI)之所以危险,在于攻击者只需控制include()file_get_contents()等函数的参数,就能读取服务器任意文件(如/etc/passwd),甚至结合文件上传实现远程代码执行,根据OWASP 2021 Top 10,此类漏洞仍属于“敏感信息泄露”与“注入”的重灾区。

本地文件包含如何防御

典型攻击手法示例
假设一个PHP页面:

$file = $_GET['page'];
include("/var/www/html/includes/" . $file . ".php");

攻击者可通过路径遍历读取系统文件:
?page=../../../../etc/passwd%00
利用空字节截断(旧版本PHP)或php://filter协议读取源码:
?page=php://filter/convert.base64-encode/resource=config

你的防线起点
防御LFI的第一原则:永远不要信任用户输入,任何从URL、表单、Cookie或HTTP头获取的文件路径参数,都必须视为潜在恶意数据。


核心防御策略:输入验证与路径白名单

1 白名单机制(最有效)

限制用户只能选择预设的合法文件,而非自由输入路径,示例:

$allowed_pages = ['home', 'about', 'contact'];
$page = $_GET['page'];
if (in_array($page, $allowed_pages)) {
    include("/templates/" . $page . ".php");
} else {
    include("/templates/error.php");
}

优势:彻底切断路径遍历与协议封装攻击。注意事项:确保白名单数组存储于安全位置(如数据库),且不可被用户修改。

2 输入过滤与净化

若必须支持动态路径(如用户上传文件预览),则需:

  • 剥离危险字符串:、、%00(空字节)、php://file://expect://等协议标识符。
  • 深度清理:使用realpath()解析最终路径,并与基线目录对比:
    $base_dir = '/var/www/uploads/';
    $path = realpath($base_dir . $_GET['file']);
    if (strpos($path, $base_dir) === 0) {
      // 安全,路径在合法目录内
    } else {
      // 路径遍历攻击
    }

3 关闭危险PHP配置

php.ini中禁用以下伪协议与函数:

allow_url_include = Off      // 禁止远程文件包含
allow_url_fopen = Off        // 禁用通过URL打开文件
disable_functions = exec, system, shell_exec, passthru // 降低代码执行风险

文件系统隔离与最小权限原则

1 将包含文件与Web根目录分离

将所有需要被包含的文件存放在Web根目录之外(如/var/includes/而非/var/www/html/includes/),即使攻击者突破路径验证,也无法通过HTTP直接访问这些文件:

include("/var/includes/templates/" . $page . ".php");

2 最小化Web服务器用户权限

  • 使用专用用户(如www-data)运行Apache/Nginx,并限制其仅能读写必要目录。
  • 被包含文件设置为只读chmod 644),避免写权限被利用上传WebShell。
  • 禁用include_path中的当前目录(),防止相对路径被劫持。

3 禁止包含目录列出

.htaccess或Nginx配置中设置:

Options -Indexes

避免用户通过目录遍历猜测包含文件名称。


WAF与运行时防护的实战配置

1 ModSecurity规则示例(Apache)

启用核心规则集(CRS)中的LFI检测规则:

# 检测路径遍历模式
SecRule ARGS "^((?:\.\./)+[^#]+)" "id:12345,phase:2,deny,status:403,msg:'LFI Path Traversal Attempt'"
# 检测伪协议使用
SecRule ARGS "(?:php://|file://|expect://|data://)" "id:12346,phase:2,deny,status:403,msg:'LFI Protocol Wrapper'

2 PHP内置过滤器与自定义检查

在项目入口使用filter_input()全局校验:

$page = filter_input(INPUT_GET, 'page', FILTER_SANITIZE_STRING);
// 或更严格的 regex
if (!preg_match('/^[a-zA-Z0-9_]+$/', $page)) {
    die('Invalid file request');
}

3 日志监控与告警

记录LFI尝试行为(如路径遍历、协议使用),并设置自动化封禁IP:

tail -f /var/log/apache2/access.log | grep -E '\.\./|php://'

结合Fail2Ban实现临时IP封锁。


问答环节:常见防御误区与终极方案

问:使用黑名单(如过滤)是否安全?

答:不完全安全。
攻击者可用多种编码绕过:

  • URL编码:%2e%2e%2f
  • 双编码:%252e%252e%252f
  • 操作系统差异:Windows下支持,Linux下(特殊环境)
    解决方案:优先使用白名单,若必须用黑名单,需结合多层解码与正则严格匹配。

问:我有WAF,是否不再需要代码层防御?

答:WAF是补充,非替代。
WAF可能被绕过(如通过POST请求、JSON格式、或利用WAF本身的配置漏洞)。多层防御是唯一正确方式:WAF拦截已知攻击 + 代码层白名单 + 文件系统隔离。

问:如何防御文件上传+LFI组合攻击?

答:分离与检查

  1. 将上传文件存储于Web根目录之外(如/var/uploads/)。
  2. 仅通过唯一ID重命名文件(避免用户知道路径)。
  3. 禁止包含用户上传文件(即使经过白名单验证,也应当使用readfile()而非include(),避免执行PHP代码)。

终极防御清单

  1. 优先白名单:规范包含请求变量,拒绝模糊路径。
  2. 标准化路径:始终使用realpath()过滤,并限制于安全目录。
  3. 禁用危险协议:关闭allow_url_include等PHP配置。
  4. 升级PHP版本:新版本(7.4+)默认禁止空字节截断等历史攻击。
  5. 定期审计代码:扫描所有includerequirefile_get_contents函数的参数来源。

LFI防御的核心不是“堵住一个洞”,而是建立从输入到文件系统的立体防线——每一层都可能失败,但多层叠加的攻击成本将让攻击者知难而退,将这些原则融入开发流程,你的应用将具备抵御绝大多数此类漏洞的能力。

抱歉,评论功能暂时关闭!