文件上传的危险在于:一旦能传可执行文件、又能访问它,就等于给了对方一个入口。下面几道锁缺一不可。
第一道:按内容判类型,不看扩展名
浏览器传来的类型和文件名都能伪造,用 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,那就更要靠「类型白名单 + 服务端命名」这两道。
第五道:别把真实路径回给前端
错误信息里不要出现服务器绝对路径;图片用相对路径或专门的输出接口。
顺手加一条:限流
上传接口按小时限制次数,避免被当成免费图床。