图片校验

This commit is contained in:
zhx 2026-07-22 19:21:15 +08:00
parent f1f4f46ee9
commit f564ecd059
3 changed files with 11 additions and 27 deletions

View File

@ -10,9 +10,6 @@ import (
"fmt"
"image/jpeg"
"image/png"
"mime"
"path/filepath"
"strings"
)
const (
@ -35,8 +32,10 @@ type Info struct {
SHA256 string
}
// Inspect 严格要求魔数、扩展名和 multipart Content-Type 三者一致,并拒绝 APNG 及畸形动画容器。
func Inspect(content []byte, filename string, partContentType string) (Info, string, error) {
// Inspect 只以完整文件字节为图片格式事实来源,并拒绝 APNG 及畸形动画容器。
// multipart 文件名和 Content-Type 都由客户端提供Android 相册缓存尤其可能保留错误后缀,
// 因而调用方必须使用这里返回的规范后缀与 Content-Type 保存对象,不能再信任上传元数据。
func Inspect(content []byte) (Info, string, error) {
var info Info
var extension string
switch {
@ -82,14 +81,6 @@ func Inspect(content []byte, filename string, partContentType string) (Info, str
if err := validateGeometry(info); err != nil {
return Info{}, "", err
}
declared, _, _ := mime.ParseMediaType(strings.TrimSpace(partContentType))
if !strings.EqualFold(declared, info.ContentType) {
return Info{}, "", fmt.Errorf("file content_type does not match signature")
}
fileExtension := strings.ToLower(strings.TrimPrefix(filepath.Ext(filename), "."))
if fileExtension != extension && !(info.Format == "jpeg" && fileExtension == "jpeg") {
return Info{}, "", fmt.Errorf("filename extension does not match signature")
}
sum := sha256.Sum256(content)
info.SizeBytes, info.SHA256 = int64(len(content)), hex.EncodeToString(sum[:])
return info, extension, nil

View File

@ -68,7 +68,9 @@ func (h *Handler) uploadUserAvatar(writer http.ResponseWriter, request *http.Req
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "file size does not match multipart metadata")
return
}
info, _, err := mediaimage.Inspect(content, header.Filename, header.Header.Get("Content-Type"))
// 文件名和 multipart Content-Type 都可能来自错误的相册缓存元数据;格式、后缀和
// 对象 Content-Type 只采用原始字节解析结果,避免真实 WebP/GIF 被误报为 JPEG 后拒绝。
info, _, err := mediaimage.Inspect(content)
if err != nil {
httpkit.WriteError(writer, request, http.StatusUnsupportedMediaType, "UNSUPPORTED_MEDIA_TYPE", err.Error())
return

View File

@ -9,9 +9,7 @@ import (
"image/jpeg"
"image/png"
"io"
"mime"
"net/http"
"path/filepath"
"strings"
roomv1 "hyapp.local/api/proto/room/v1"
@ -151,7 +149,9 @@ func (h *Handler) uploadRoomMedia(writer http.ResponseWriter, request *http.Requ
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "file size does not match multipart metadata")
return
}
media, _, err := inspectRoomImage(content, header.Filename, header.Header.Get("Content-Type"))
// 相册缓存文件名和 multipart Content-Type 不可信;格式、后缀和对象 Content-Type
// 全部从原始字节解析,确保 WebP/GIF 即使带着 JPG 元数据也能按真实类型保存。
media, _, err := inspectRoomImage(content)
if err != nil {
httpkit.WriteError(writer, request, http.StatusUnsupportedMediaType, "UNSUPPORTED_MEDIA_TYPE", err.Error())
return
@ -237,7 +237,7 @@ func (h *Handler) uploadRoomMedia(writer http.ResponseWriter, request *http.Requ
})
}
func inspectRoomImage(content []byte, filename string, partContentType string) (roomMediaData, string, error) {
func inspectRoomImage(content []byte) (roomMediaData, string, error) {
var data roomMediaData
var extension string
switch {
@ -280,15 +280,6 @@ func inspectRoomImage(content []byte, filename string, partContentType string) (
default:
return data, "", fmt.Errorf("only jpeg, png, gif and webp are supported")
}
declared, _, _ := mime.ParseMediaType(strings.TrimSpace(partContentType))
if !strings.EqualFold(declared, data.ContentType) {
return roomMediaData{}, "", fmt.Errorf("file content_type does not match signature")
}
fileExtension := strings.ToLower(strings.TrimPrefix(filepath.Ext(filename), "."))
validExtension := fileExtension == extension || (data.Format == "jpeg" && fileExtension == "jpeg")
if !validExtension {
return roomMediaData{}, "", fmt.Errorf("filename extension does not match signature")
}
sum := sha256.Sum256(content)
data.SizeBytes, data.SHA256, data.Status = int64(len(content)), hex.EncodeToString(sum[:]), "active"
return data, extension, nil