ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

文件上传的三种白名单,和3种黑名单,以及3种针对文件内容的修改,特性、get,post传参方式区别,正则匹配,以及高亮函数作用

文件上传的三种白名单,和3种黑名单,以及3种针对文件内容的修改,特性、get,post传参方式区别,正则匹配,以及高亮函数作用

一、黑白名单

(一)3 种白名单校验方式

白名单核心:只放行符合规则的文件,其余全部拒绝。

  1. 后缀名白名单

    • 逻辑:预定义允许上传的文件后缀(如.jpg/.png/.pdf),仅校验文件名后缀匹配时放行。
    • 示例(PHP):

      php

      运行

      $allowedExts = ['jpg', 'png', 'gif']; $ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION); if (!in_array(strtolower($ext), $allowedExts)) { die("仅允许上传图片文件"); }
    • 特点:最常用,但需注意大小写(如.JPG转小写)、多后缀绕过(如test.jpg.php)。
  2. MIME 类型白名单

    • 逻辑:校验 HTTP 请求头中Content-Type,仅允许指定类型(如image/jpeg、image/png)。
    • 示例(PHP):

      php

      运行

      $allowedMime = ['image/jpeg', 'image/png', 'image/gif']; $mime = $_FILES['file']['type']; if (!in_array($mime, $allowedMime)) { die("MIME类型非法"); }
    • 特点:易被抓包篡改(如把application/x-php改为image/jpeg),需配合其他校验。
  3. 文件头(魔数)白名单

    • 逻辑:校验文件二进制开头的固定标识(魔数),如 JPG 以FF D8 FF开头、PNG 以89 50 4E 47开头,仅匹配放行。
    • 示例(PHP):

      php

      运行

      $file = fopen($_FILES['file']['tmp_name'], 'rb'); $header = fread($file, 4); // 读取前4字节 fclose($file); $allowedHeader = ['ffd8ff', '89504e47', '47494638']; // JPG/PNG/GIF if (!in_array(bin2hex($header), $allowedHeader)) { die("文件内容非法"); }
    • 特点:最可靠的白名单方式,直接校验文件实际内容,不易被绕过。
(二)3 种黑名单校验方式(禁止特定内容)

黑名单核心:拒绝指定的危险内容,其余放行(风险高,易被绕过)。

  1. 后缀名黑名单

    • 逻辑:禁止危险后缀(如.php/.php5/.jsp),仅拒绝匹配的后缀。
    • 示例(PHP):

      php

      运行

      $forbidExts = ['php', 'php5', 'php7', 'jsp']; $ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION); if (in_array(strtolower($ext), $forbidExts)) { die("禁止上传脚本文件"); }
    • 绕过方式:使用黑名单外的后缀(如.php4/.phtml)、空格 / 点绕过(test.php.)、大小写(Test.PHP)。
  2. 关键词黑名单

    • 逻辑:扫描文件名 / 文件内容中是否包含危险关键词(如<?php、eval(、system(),包含则拒绝。
    • 示例(PHP):

      php

      运行

      $content = file_get_contents($_FILES['file']['tmp_name']); $forbidKeywords = ['<?php', 'eval(', 'system(']; foreach ($forbidKeywords as $kw) { if (strpos($content, $kw) !== false) { die("文件包含危险代码"); } }
    • 绕过方式:编码(如base64_decode)、变形(<?=替代<?php)、注释截断。
  3. 文件大小黑名单(反向限制)

    • 逻辑:禁止超大文件(如超过 10MB),本质是 “拒绝不符合大小的文件”,属于黑名单逻辑。
    • 示例(PHP):

      php

      运行

      if ($_FILES['file']['size'] > 10 * 1024 * 1024) { die("文件超过10MB,禁止上传"); }
    • 绕过方式:分片上传、压缩后上传再解压。
(三)3 种针对文件内容的修改(绕过 / 加固)

文件内容修改是上传漏洞的核心利用 / 防御手段,常见方式:

  1. 图片马(文件内容拼接)

    • 逻辑:将 PHP 代码嵌入图片文件尾部 / 头部,保留图片魔数(绕过文件头校验),上传后通过解析漏洞执行。
    • 示例:

      bash

      运行

      # 命令行拼接:将shell.php内容写入test.jpg尾部 copy /b test.jpg + shell.php webshell.jpg
    • 防御:上传后重新编码图片(如imagecreatefromjpeg重新生成),清除嵌入的代码。
  2. 代码编码 / 混淆(绕过关键词检测)

    • 逻辑:对危险代码进行 base64、十六进制编码,上传后通过解码函数执行。
    • 示例(PHP):

      php

      运行

      # 混淆后的代码(绕过关键词检测) <?php eval(base64_decode('ZXZhbCgkX1BPU1RbJ2MnXSk7')); ?>
    • 防御:解码后再检测关键词、限制eval/assert等危险函数。
  3. 文件内容截断(绕过后缀校验)

    • 逻辑:利用 0x00 截断(PHP < 5.3.4)或换行符,修改文件名 / 内容中的后缀标识。
    • 示例(抓包修改):
      • 文件名:test.php%00.jpg(0x00 截断,服务器解析为test.php);
      • 内容:在代码后加\0截断,绕过关键词扫描。
    • 防御:升级 PHP 版本、过滤文件名中的特殊字符(%00、\0、换行符)。

二、PHP 特性 & GET/POST 传参区别(抓包视角)

(一)核心 PHP 特性(与文件上传相关)
  1. 文件上传相关超全局变量:$_FILES(存储上传文件信息:name/type/tmp_name/size/error);
  2. 解析特性:
    • .php后缀的文件会被 PHP 解析器执行,.phtml/.php5等后缀若配置了AddType application/x-httpd-php .phtml也会解析;
    • 短标签<?= ?>等价于<?php echo ?>,可绕过<?php关键词检测;
  3. 路径处理:pathinfo()取后缀时,若文件名含多个点(如test.php.jpg),仅取最后一个后缀;
  4. 临时文件:上传的文件先存到php.ini中upload_tmp_dir目录,脚本结束后自动删除,需手动移动(move_uploaded_file)。
(二)GET/POST 传参区别(抓包对比)
维度GET 传参POST 传参
抓包位置请求行(URL 后),如GET /test.php?name=123 HTTP/1.1请求体(Header 后空行),如name=123&age=20
数据类型仅 ASCII 字符,无法传二进制支持任意类型(二进制 / 文件),如multipart/form-data
长度限制受浏览器 / 服务器限制(如 URL 长度≤2KB)无默认限制(可通过php.ini的post_max_size配置)
抓包示例(文件上传)无法传文件(仅 URL 参数)Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryxxx包含文件二进制内容
参数可见性暴露在 URL 中(日志 / 历史记录)隐藏在请求体中(相对安全)
PHP 接收方式$_GET['name']或$_REQUEST$_POST['name']或$_FILES(文件)

三、正则匹配 & 高亮函数作用

(一)正则匹配(文件上传中常用)

正则是校验文件名 / 内容的核心手段,PHP 中常用函数:preg_match()、preg_replace()。

  1. 常见正则示例:
    • 校验图片后缀:/^\.(jpg|png|gif)$/i(i 表示忽略大小写);
    • 检测 PHP 标签:/<\?php/i(匹配<?php,忽略大小写);
    • 过滤特殊字符:preg_replace('/[^\w\.]/', '', $filename)(仅保留字母 / 数字 / 下划线 / 点)。
  2. 注意事项:
    • 正则边界:必须加^(开头)和$(结尾),否则会匹配部分内容(如/jpg/会匹配testjpg.txt);
    • 贪婪匹配:默认贪婪,需用?非贪婪(如/\.(php).*$/会匹配test.php.jpg的.php.jpg)。
(二)高亮函数作用

PHP 高亮函数用于代码语法高亮,核心是highlight_file()/highlight_string(),在文件上传漏洞中需重点关注:

  1. 功能:
    • highlight_file($filename):输出指定文件的内容,并按 PHP 语法高亮(添加 HTML 标签);
    • highlight_string($str):高亮字符串中的 PHP 代码。
  2. 安全风险:
    • 若未校验权限,攻击者可通过highlight_file($_GET['file'])读取服务器任意文件(如highlight_file('/etc/passwd'));
    • 若上传的图片马被高亮函数输出,会暴露嵌入的 PHP 代码(但不会执行,仅泄露)。
  3. 防御:
    • 限制高亮的文件路径(如仅允许./upload/目录);
    • 过滤../等路径遍历字符。

总结

  1. 文件上传安全:白名单优先(魔数 + 后缀 + MIME),黑名单易被绕过;文件内容修改核心是 “保留合法标识 + 嵌入危险代码”;
  2. GET/POST 核心区别:GET 传参在 URL(短 / ASCII),POST 在请求体(支持大文件 / 二进制);
  3. 正则匹配需注意边界和贪婪模式,高亮函数需限制路径避免文件读取漏洞。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进