文件上传的危险在于:一旦能传可执行文件、又能访问它,就等于给了对方一个入口。下面几道锁缺一不可。

第一道:按内容判类型,不看扩展名

浏览器传来的类型和文件名都能伪造,用 getimagesize() 这类函数读文件头判断:

$info  = @getimagesize($tmp_path);
$allow = array(IMAGETYPE_JPEG => 'jpg', IMAGETYPE_PNG => 'png', IMAGETYPE_GIF => 'gif');

if (!$info || !isset($allow[$info[2]])) {
    exit('只支持 jpg / png / gif');
}

特别注意 SVG:它是 XML,可以内嵌脚本,等于 XSS。个人站点一般直接不收。

第二道:文件名由服务端生成

原文件名里可能带 ../、空格、特殊字符。统一改成「日期 + 随机串 + 后缀」,原文件名只用来生成标题。

第三道:限制体积

  • PHP 的 upload_max_filesize 与 post_max_size 要一起看 —— 超过 post_max_size 时整个请求会被丢掉
  • 代码里再判一次,并给用户看得懂的提示("原图 6.2MB,超过上限 2MB")

第四道:上传目录不许执行

这是最重要的一道。在 uploads/ 里放一个 .htaccess:

php_flag engine off
AddType text/plain .php .phtml .php5 .php7 .pht
Options -ExecCGI

Nginx 环境则在站点配置里对该目录加 location 限制。注意部分主机(例如 Kangle)不读 .htaccess,那就更要靠「类型白名单 + 服务端命名」这两道。

第五道:别把真实路径回给前端

错误信息里不要出现服务器绝对路径;图片用相对路径或专门的输出接口。

顺手加一条:限流

上传接口按小时限制次数,避免被当成免费图床。