一、黑白名单
(一)3 种白名单校验方式
白名单核心:只放行符合规则的文件,其余全部拒绝。
后缀名白名单
- 逻辑:预定义允许上传的文件后缀(如
.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)。
- 逻辑:预定义允许上传的文件后缀(如
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),需配合其他校验。
- 逻辑:校验 HTTP 请求头中
文件头(魔数)白名单
- 逻辑:校验文件二进制开头的固定标识(魔数),如 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("文件内容非法"); } - 特点:最可靠的白名单方式,直接校验文件实际内容,不易被绕过。
- 逻辑:校验文件二进制开头的固定标识(魔数),如 JPG 以
(二)3 种黑名单校验方式(禁止特定内容)
黑名单核心:拒绝指定的危险内容,其余放行(风险高,易被绕过)。
后缀名黑名单
- 逻辑:禁止危险后缀(如
.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)。
- 逻辑:禁止危险后缀(如
关键词黑名单
- 逻辑:扫描文件名 / 文件内容中是否包含危险关键词(如
<?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)、注释截断。
- 逻辑:扫描文件名 / 文件内容中是否包含危险关键词(如
文件大小黑名单(反向限制)
- 逻辑:禁止超大文件(如超过 10MB),本质是 “拒绝不符合大小的文件”,属于黑名单逻辑。
- 示例(PHP):
php
运行
if ($_FILES['file']['size'] > 10 * 1024 * 1024) { die("文件超过10MB,禁止上传"); } - 绕过方式:分片上传、压缩后上传再解压。
(三)3 种针对文件内容的修改(绕过 / 加固)
文件内容修改是上传漏洞的核心利用 / 防御手段,常见方式:
图片马(文件内容拼接)
- 逻辑:将 PHP 代码嵌入图片文件尾部 / 头部,保留图片魔数(绕过文件头校验),上传后通过解析漏洞执行。
- 示例:
bash
运行
# 命令行拼接:将shell.php内容写入test.jpg尾部 copy /b test.jpg + shell.php webshell.jpg - 防御:上传后重新编码图片(如
imagecreatefromjpeg重新生成),清除嵌入的代码。
代码编码 / 混淆(绕过关键词检测)
- 逻辑:对危险代码进行 base64、十六进制编码,上传后通过解码函数执行。
- 示例(PHP):
php
运行
# 混淆后的代码(绕过关键词检测) <?php eval(base64_decode('ZXZhbCgkX1BPU1RbJ2MnXSk7')); ?> - 防御:解码后再检测关键词、限制
eval/assert等危险函数。
文件内容截断(绕过后缀校验)
- 逻辑:利用 0x00 截断(PHP < 5.3.4)或换行符,修改文件名 / 内容中的后缀标识。
- 示例(抓包修改):
- 文件名:
test.php%00.jpg(0x00 截断,服务器解析为test.php); - 内容:在代码后加
\0截断,绕过关键词扫描。
- 文件名:
- 防御:升级 PHP 版本、过滤文件名中的特殊字符(
%00、\0、换行符)。
二、PHP 特性 & GET/POST 传参区别(抓包视角)
(一)核心 PHP 特性(与文件上传相关)
- 文件上传相关超全局变量:
$_FILES(存储上传文件信息:name/type/tmp_name/size/error); - 解析特性:
.php后缀的文件会被 PHP 解析器执行,.phtml/.php5等后缀若配置了AddType application/x-httpd-php .phtml也会解析;- 短标签
<?= ?>等价于<?php echo ?>,可绕过<?php关键词检测;
- 路径处理:
pathinfo()取后缀时,若文件名含多个点(如test.php.jpg),仅取最后一个后缀; - 临时文件:上传的文件先存到
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()。
- 常见正则示例:
- 校验图片后缀:
/^\.(jpg|png|gif)$/i(i 表示忽略大小写); - 检测 PHP 标签:
/<\?php/i(匹配<?php,忽略大小写); - 过滤特殊字符:
preg_replace('/[^\w\.]/', '', $filename)(仅保留字母 / 数字 / 下划线 / 点)。
- 校验图片后缀:
- 注意事项:
- 正则边界:必须加
^(开头)和$(结尾),否则会匹配部分内容(如/jpg/会匹配testjpg.txt); - 贪婪匹配:默认贪婪,需用
?非贪婪(如/\.(php).*$/会匹配test.php.jpg的.php.jpg)。
- 正则边界:必须加
(二)高亮函数作用
PHP 高亮函数用于代码语法高亮,核心是highlight_file()/highlight_string(),在文件上传漏洞中需重点关注:
- 功能:
highlight_file($filename):输出指定文件的内容,并按 PHP 语法高亮(添加 HTML 标签);highlight_string($str):高亮字符串中的 PHP 代码。
- 安全风险:
- 若未校验权限,攻击者可通过
highlight_file($_GET['file'])读取服务器任意文件(如highlight_file('/etc/passwd')); - 若上传的图片马被高亮函数输出,会暴露嵌入的 PHP 代码(但不会执行,仅泄露)。
- 若未校验权限,攻击者可通过
- 防御:
- 限制高亮的文件路径(如仅允许
./upload/目录); - 过滤
../等路径遍历字符。
- 限制高亮的文件路径(如仅允许
总结
- 文件上传安全:白名单优先(魔数 + 后缀 + MIME),黑名单易被绕过;文件内容修改核心是 “保留合法标识 + 嵌入危险代码”;
- GET/POST 核心区别:GET 传参在 URL(短 / ASCII),POST 在请求体(支持大文件 / 二进制);
- 正则匹配需注意边界和贪婪模式,高亮函数需限制路径避免文件读取漏洞。