⚠ 平台测试中。

🔐 后台上传图片:安全校验与自动转 AVIF

最后更新:2026-10-02

🎯 设计目标

诉求实现方式
体积最小、加载最快浏览器端 Canvas 把图片转成 image/avif(优先)/ image/webp 再上传
防 XSS / 伪装文件服务端用 image.DecodeConfig + 文件魔数校验真实格式,伪装文件直接拒收删除
部署零额外依赖不用服务端 CGO(客户机无 gcc),转码完全在浏览器,Windows 部署无忧
低版本浏览器防护不支持 AVIF 编码的浏览器禁止进入 /admin,提示更新浏览器
logo / 微信二维码不压缩这两个 input 标记 data-nocompress,原图直传、保持清晰度

⚙️ 浏览器端转换规则

后台所有「上传图片」的 <input type=file> 都会被前端脚本拦截,流程如下:

步骤行为
1. 读取用 FileReader 读入,画到 canvas
2. 缩放等比缩放到最大 1000×900(超出则缩小,不足则保持原尺寸)
3. 编码优先 canvas.toBlob('image/avif');浏览器不支持 AVIF 时回退 image/webp,再回退 image/png
4. 提交把得到的 Blob 当作文件上传到 /admin/upload/image

注意:带 data-nocompress 的输入框(站点 logo、企业微信二维码)会跳过上述缩放与重编码,原图直接上传,保证图标清晰不被压糊。

富文本编辑器(wangeditor)内插图也走同一套转换:通过 customUpload 钩子先调 window.cmsCompressImage,再 fetch 上传,所以正文配图同样会被转 AVIF。

📋 上传白名单

后台「上传图片」仅允许以下扩展名;其余一律拒绝(返回错误,不落盘)。

类别扩展名
允许上传.jpg .jpeg .png .gif .webp .avif
已移除.svg(有 XSS 注入风险)、.bmp(体积大、无压缩)

这与「前台可引用的静态资源扩展名」不是一回事——.svg 虽不能作为前台模板引用(见 模板打包规范),但仍可作为静态资源手动放进 /static/ 引用,只是不能经后台上传。

🛡️ 服务端真实类型校验(兜底)

前端能被绕过(直接构造请求),所以服务端对每张上传图做真实格式校验,这是安全闭环的最后一环:

  • JPG / PNG / GIF:用 image.DecodeConfig 真正解码一帧,拿不到尺寸即判为伪造成功拒绝
  • WebP:校验文件头魔数 RIFF....WEBP
  • AVIF:校验文件头 ftyp 后跟 avif / avis
  • 校验不通过的伪装文件:立即 os.Remove 删除,不上传目录、不入库

因此即便攻击者伪造 .jpg 后缀塞入恶意内容,也会被服务端拦截,不会存进网站目录。

🚪 AVIF 浏览器门槛(/admin 守卫)

进入后台前,页面会执行一次真编码能力探测(不只是特性判断,而是实际 canvas.toBlob('image/avif') 看是否真能产出 type==='image/avif' 的 Blob),防止浏览器静默回退成 PNG 却自认支持。

探测结果用户看到什么
支持 AVIF 编码正常显示登录框 / 后台,可继续操作
不支持全屏红字遮罩「请更新浏览器到最新版 Chrome / Edge / Firefox」,隐藏登录框;已进入后台的页面顶部显示红条并禁用上传控件

这一步保证:凡是能进 /admin 的浏览器,都具备把图片转 AVIF 的能力,从而让整条「浏览器端压缩转 AVIF」链路成立。

🔧 可调参数(环境变量)

压缩尺寸与质量可通过环境变量覆盖(缺省取下方默认值)。

默认值 1000×900、质量 82 已在源码中写死为 fallback,不设置也生效。改完后重启服务进程即可生效(这是服务端配置,不是模板热加载)。

❓ 常见疑问

疑问解答
服务器要装 ImageMagick / libvips 吗?不需要。转码全在浏览器,服务端只校验,零额外依赖。
客户上传的是 PNG,前台会变成 AVIF 吗?会。浏览器转成 AVIF(不支持则 WebP)后上传,落盘的就是 AVIF。
logo 被压糊了怎么办?站点 logo、企业微信 两个 input 已标记 data-nocompress,原图直传不缩放。
老浏览器上传报错?属正常防护——提示更新浏览器即可,这是安全设计。
直接 POST 上传接口能绕过前端吗?不能。服务端真实类型校验会拦截伪装文件并删除。
本网站使用 Cookie 以提升浏览体验,继续浏览即表示您同意我们的 Cookie 政策。