为什么要禁执行
文件上传漏洞的最终危害来自「上传的文件被 Web 服务器当脚本解析」这一步。上传前的类型校验再细,也有绕过的可能,所以必须在服务器层让上传目录无法执行脚本,形成第二道防线。Nginx 在 location 层拒绝脚本扩展名,Apache 用 RemoveHandler 关闭目录内脚本处理,IIS 用请求筛选拒绝可执行扩展,三者思路一致。
基本信息
| 适用组件 | Nginx、Apache HTTP Server、IIS |
|---|---|
| 配置位置 | 独立上传目录 + location 或 Directory 规则 |
| 效果 | 命中脚本扩展名的请求返回 403,或文件只被当静态内容返回 |
| 前置条件 | 上传目录与静态资源目录分离,使用独立路径前缀 |
| 验证方式 | 实际上传一个探测脚本并请求它,应返回 403 或原样下载 |
| 配套措施 | 上传前类型白名单、随机文件名、存储与服务分离 |
Nginx:上传目录禁执行
Nginx:上传目录禁执行
# 上传目录使用独立路径前缀,便于统一加规则
location ^~ /uploads/ {
# 命中脚本扩展名一律拒绝(匹配大小写不敏感)
location ~* \.(php|php5|phtml|phar|jsp|asp|aspx|sh)$ {
deny all;
}
autoindex off; # 关闭目录列表,减少信息暴露
}Apache:关闭目录内脚本处理
Apache:关闭目录内脚本处理
<Directory /var/www/html/uploads>
Options -ExecCGI -Indexes # 关闭 CGI 执行与目录列表
RemoveHandler .php .phtml .php5 # 移除脚本处理器绑定
RemoveType .php .phtml .php5 # 移除 MIME 类型映射
php_flag engine off # 明确关闭 PHP 引擎
Require all granted # 只允许静态读取
</Directory>常见绕过与对策
| 绕过手法 | 说明 | 对策 |
|---|---|---|
| 扩展名大小写与变体 | 使用 .PhP、.phtml、.php5 等未被拦截的写法 | 正则匹配保持大小写不敏感并覆盖全部脚本扩展名 |
| 双扩展名 | 上传 shell.php.jpg 依赖服务器解析规则 | 在目录层禁用脚本处理,不依赖扩展名判断 |
| 目录解析差异 | 多扩展名解析把 .php.jpg 当脚本执行 | 关闭目录内脚本执行而非仅做扩展名黑名单 |
| 上传配置文件 | 上传 .htaccess、.user.ini 改写目录解析行为 | 在 location 层拒收点开头的文件,禁用目录级配置覆盖 |
| 换个上传点 | 功能模块各自的上传目录未纳入规则 | 上传目录统一收敛到同一路径前缀 |
改完必须实际验证
改完必须实际验证
# 上传一个探测脚本后请求它,期望返回 403
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/uploads/probe.php
# 请求同目录下的其他脚本扩展名,确认规则不是只拦一种
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/uploads/probe.phtml
# 复查 Nginx 最终生效配置里 uploads 段落的层级
nginx -T | grep -A 8 'uploads/'上传目录与静态资源目录分离,用独立路径前缀,既方便加统一规则,也方便日志与审计时单独统计。
服务端禁执行是兜底措施,不是唯一措施。上传入口仍需做类型白名单、内容嗅探、随机文件名与存储分离,多层叠加才能覆盖解析漏洞与配置替换类绕过。