From 6ea4e5365e578607e8b21ab1cf842c86ed20d3aa Mon Sep 17 00:00:00 2001 From: Victor_Jay Date: Sun, 31 May 2026 03:04:45 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=96=87=E7=AB=A0=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E5=A2=9E=E5=8A=A0=E5=86=85=E5=AE=B9=E8=A7=A3?= =?UTF-8?q?=E7=A0=81=E9=AA=8C=E8=AF=81=EF=BC=8C=E9=98=B2=E4=BC=AA=E6=89=A9?= =?UTF-8?q?=E5=B1=95=E5=90=8D=E5=92=8C=E5=9B=BE=E7=89=87=E6=8A=95=E6=AF=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 所有格式(JPEG/PNG/GIF/WebP)上传后强制解码验证是否为有效图片 - 解码失败直接拒绝,不再回退原样存储 - 消除 .php 改名为 .jpg 绕过检查的安全漏洞 - JPEG/PNG 解码与 WebP 编码合并为一次解码,避免重复读 --- internal/controller/post_controller.go | 49 +++++++++++++++++++------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/internal/controller/post_controller.go b/internal/controller/post_controller.go index a4d2a50..3e83233 100644 --- a/internal/controller/post_controller.go +++ b/internal/controller/post_controller.go @@ -411,25 +411,48 @@ func (ctrl *PostController) UploadImage(c *gin.Context) { ts := time.Now().UnixMilli() var savePath, url string - // JPEG/PNG → WebP 压缩存储(保留原尺寸,quality 80) - if ext == ".jpg" || ext == ".jpeg" || ext == ".png" { + isJPEGPNG := ext == ".jpg" || ext == ".jpeg" || ext == ".png" + isWebP := ext == ".webp" + isGIF := ext == ".gif" + + // 强制解码验证文件是否为有效图片(防伪扩展名、图片投毒),验证失败直接拒绝 + // JPEG/PNG:解码后顺便做 WebP 压缩 + if isJPEGPNG { img, _, err := image.Decode(bytes.NewReader(data)) - if err == nil { - var buf bytes.Buffer - if err := webp.Encode(&buf, img, &webp.Options{Quality: 80}); err == nil { - // 仅当 WebP 比原文件小时才采用,否则回退原格式 - if buf.Len() > 0 && buf.Len() < len(data) { - filename := fmt.Sprintf("%d_%d.webp", uid, ts) - savePath = filepath.Join(storageDir, filename) - if err := os.WriteFile(savePath, buf.Bytes(), 0644); err == nil { - url = "/uploads/posts/" + filename - } + if err != nil { + common.Error(c, http.StatusBadRequest, "图片格式无效,请上传有效的图片文件") + return + } + // WebP 压缩存储(保留原尺寸,quality 80) + var buf bytes.Buffer + if err := webp.Encode(&buf, img, &webp.Options{Quality: 80}); err == nil { + if buf.Len() > 0 && buf.Len() < len(data) { + filename := fmt.Sprintf("%d_%d.webp", uid, ts) + savePath = filepath.Join(storageDir, filename) + if err := os.WriteFile(savePath, buf.Bytes(), 0644); err == nil { + url = "/uploads/posts/" + filename } } } } - // 回退:GIF / WebP / 转换失败 / 转换后反而更大 → 原样存储 + // GIF:仅解码验证,不转换 + if isGIF { + if _, _, err := image.Decode(bytes.NewReader(data)); err != nil { + common.Error(c, http.StatusBadRequest, "图片格式无效,请上传有效的图片文件") + return + } + } + + // WebP:仅解码验证,不重复编码 + if isWebP { + if _, err := webp.Decode(bytes.NewReader(data)); err != nil { + common.Error(c, http.StatusBadRequest, "图片格式无效,请上传有效的图片文件") + return + } + } + + // 原格式存储(WebP / GIF / 转换后反而更大) if savePath == "" { filename := fmt.Sprintf("%d_%d%s", uid, ts, ext) savePath = filepath.Join(storageDir, filename)