fix: 文章图片上传增加内容解码验证,防伪扩展名和图片投毒
- 所有格式(JPEG/PNG/GIF/WebP)上传后强制解码验证是否为有效图片 - 解码失败直接拒绝,不再回退原样存储 - 消除 .php 改名为 .jpg 绕过检查的安全漏洞 - JPEG/PNG 解码与 WebP 编码合并为一次解码,避免重复读
This commit is contained in:
@ -411,25 +411,48 @@ func (ctrl *PostController) UploadImage(c *gin.Context) {
|
|||||||
ts := time.Now().UnixMilli()
|
ts := time.Now().UnixMilli()
|
||||||
var savePath, url string
|
var savePath, url string
|
||||||
|
|
||||||
// JPEG/PNG → WebP 压缩存储(保留原尺寸,quality 80)
|
isJPEGPNG := ext == ".jpg" || ext == ".jpeg" || ext == ".png"
|
||||||
if ext == ".jpg" || ext == ".jpeg" || ext == ".png" {
|
isWebP := ext == ".webp"
|
||||||
|
isGIF := ext == ".gif"
|
||||||
|
|
||||||
|
// 强制解码验证文件是否为有效图片(防伪扩展名、图片投毒),验证失败直接拒绝
|
||||||
|
// JPEG/PNG:解码后顺便做 WebP 压缩
|
||||||
|
if isJPEGPNG {
|
||||||
img, _, err := image.Decode(bytes.NewReader(data))
|
img, _, err := image.Decode(bytes.NewReader(data))
|
||||||
if err == nil {
|
if err != nil {
|
||||||
var buf bytes.Buffer
|
common.Error(c, http.StatusBadRequest, "图片格式无效,请上传有效的图片文件")
|
||||||
if err := webp.Encode(&buf, img, &webp.Options{Quality: 80}); err == nil {
|
return
|
||||||
// 仅当 WebP 比原文件小时才采用,否则回退原格式
|
}
|
||||||
if buf.Len() > 0 && buf.Len() < len(data) {
|
// WebP 压缩存储(保留原尺寸,quality 80)
|
||||||
filename := fmt.Sprintf("%d_%d.webp", uid, ts)
|
var buf bytes.Buffer
|
||||||
savePath = filepath.Join(storageDir, filename)
|
if err := webp.Encode(&buf, img, &webp.Options{Quality: 80}); err == nil {
|
||||||
if err := os.WriteFile(savePath, buf.Bytes(), 0644); err == nil {
|
if buf.Len() > 0 && buf.Len() < len(data) {
|
||||||
url = "/uploads/posts/" + filename
|
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 == "" {
|
if savePath == "" {
|
||||||
filename := fmt.Sprintf("%d_%d%s", uid, ts, ext)
|
filename := fmt.Sprintf("%d_%d%s", uid, ts, ext)
|
||||||
savePath = filepath.Join(storageDir, filename)
|
savePath = filepath.Join(storageDir, filename)
|
||||||
|
|||||||
Reference in New Issue
Block a user