⭐ 推荐:社区规则条款 V1.0

三闸防线详解:白名单剔除、落盘前拒绝与 .htaccess 同口径

CLARA轻量论坛系统
CLARA轻量论坛系统 星耀SVIP星耀SVIP管理员管理员 黑卡会员黑卡会员
发布于 2026-10-09 20:00 ·9 浏览 ·0 回复

学完这篇你能搞清楚一次文件上传要过几道关、每道关各管什么,以及为什么只做一层校验迟早会出事。

文件上传是论坛最容易被拿下的入口。攻击思路说起来很朴素:把一段可执行代码塞进一个看起来像图片的文件,传上去,再想办法让它跑起来。所以防御不能只靠一层,要按「类型判断 → 落地时机 → 执行权限」排成三道闸。下面按顺序讲。

第一步:白名单剔除,先决定"什么东西根本不许进来"

这一步做的是准入判断,发生在任何字节被写进磁盘之前。

核心逻辑是"剔除"而不是"拦截":维护一份允许的扩展名清单,凡是取不到扩展名、或者扩展名不在清单里的,直接从候选里划掉,连讨论的余地都没有。用白名单而不是黑名单,是因为黑名单永远列不全(php5、phtml、pht、phar……),而白名单的边界是封闭的。

在 Clara BBS 里,这个清单有两个互相独立的字段,在后台「系统设置 → 上传」:

  • 允许的扩展名:管图片,例如 jpg、png、gif、webp
  • 附件允许的扩展名:管附件,例如 zip、rar、pdf、txt

注意:这两个字段不联动。很多人只改了"允许的扩展名"就往帖子里传 zip,然后一直失败——因为附件走的是另一个字段。图片上传还是会员权益,普通用户会看到"会员专属"提示,管理员豁免,这是两码事,别混。

取扩展名时要做归一化,否则形同虚设:

  • 统一转小写,防止 .PHP 绕过
  • 以最后一个点为准,shell.php.jpg 得到的是 jpg
  • 丢掉路径信息和空字节,防止 x.php%00.jpg 这类花招
  • 文件名里的中文、空格、特殊符号统一替换成安全字符

同时不要只看前端传过来的 MIME 类型——那是客户端说的,可以随便伪造。服务端判断类型要落到文件内容上,图片还要做二次校验(真正读一遍头部信息,确认它确实是张图片、尺寸解析得出来),校验不过照样剔除。

第二步:落盘前拒绝,失败时磁盘上不留任何痕迹

这一步管的是顺序。

正确的顺序是:收文件 → 全部校验通过 → 才写入 uploads 目录。任何一项不通过,直接在内存里结束,返回具体原因。

常见错误写法是先落盘再检查:

move_uploaded_file(...);   // 先写进去
if (!在白名单里) unlink(...);  // 再删掉

这中间有个窗口期。并发请求、异常中断、删除失败,都可能让一个非法文件留在目录里。既然最终要删,那干脆别写。

注意:落盘失败时页面会告诉你是哪一类原因,比如超过 PHP 上传上限、扩展名不在白名单、目录不可写——顺着提示排查,比反复试快得多。

如果落盘这一步就是失败,先查三件事:

  1. 白名单字段是否包含该格式(图片字段和附件字段分开看)
  2. PHP 的 upload_max_filesize 与 post_max_size 是否大于文件体积。宝塔默认 2M,手机直出的照片随便就超,建议调到 30M 以上
  3. uploads 目录是否可写

第三步:.htaccess 同口径,落地了也不让它跑起来

前两道闸的前提是"判断永远正确"。但安全设计不能建立在假设上,所以还要有第三道:假设某个文件真的落到了 uploads 目录,它也不能被执行。

做法是在上传目录放一份 .htaccess,关掉脚本执行能力,只允许静态读取。所谓"同口径",意思是这里的规则要和第一步的白名单对齐——白名单之外的类型不让落地,落地目录也不给任何执行权限,两层判断用的是同一套边界,不会出现"类型放行了但执行没关"或者反过来的错位。

Apache 环境下大致是关掉该目录的脚本处理器、限定只能读取常见静态资源类型这一类写法。Nginx 没有 .htaccess,等价做法是在站点配置的 location 里对 uploads 路径单独声明:不做 PHP 转发,只当静态文件发出去。用宝塔的话,面板里对应的就是站点的伪静态/安全配置项。

注意:伪静态那块别搞混。Clara BBS 会自动识别 .html 后缀的 URL,服务器只需要把非静态文件的请求转发到 index.php,整站层面不需要写一堆规则。上面说的 uploads 目录限制是独立的一条,别指望伪静态规则顺带解决它。

三道闸的关系可以一句话概括:**白名单决定谁能来,落盘前校验决定什么时候落地,.htaccess 决定落地之后能干什么。** 任何一层单独拿出来都不够看,三层叠在一起才是完整的闭环。

小结

  • 白名单要"剔除"思维,边界封闭;取扩展名务必归一化:小写、取最后一个点、丢路径与空字节、重命名非法字符
  • 图片扩展名和附件扩展名是两个独立字段,图片还要做内容级二次校验,不认客户端声明的 MIME
  • 校验必须全部通过再落盘,杜绝"先写后删"的窗口期
  • 上传失败页面会给具体原因,先按提示查白名单字段、PHP 体积上限、目录权限
  • 上传目录要单独禁执行;Apache 用 .htaccess,Nginx 用 location 限制,和整站伪静态是两回事
  • 会员权益、上传限制、文件类型是三套不同的开关,排查时别串线
本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-767.html
转载请注明出处,版权归原作者所有。

全部回复 0

还没有回复,来抢沙发~