Контекст: UploadHandler.UploadFile валидировал только header.Header.Get("Content-Type") из multipart-части. Этот заголовок полностью контролируется клиентом, поэтому под видом image/jpeg можно было залить произвольный файл (HTML/JS/SVG/исполняемый контент) в общий бакет.
Суть: Заявленный content-type — это утверждение клиента, а не факт. Доверять можно только реальным байтам файла. Проверяем сигнатуру по первым 512 байтам и затем возвращаем курсор в начало (Seek(0,0)), иначе загрузка прочитает усечённый файл.
func verifyImageSignature(file io.ReadSeeker, declaredType string) error {
head := make([]byte, 512)
n, _ := io.ReadFull(file, head)
head = head[:n]
file.Seek(0, io.SeekStart) // вернуть курсор для PutObject
if !matchesImageType(head, declaredType) {
return errors.New("file signature mismatch")
}
return nil
}
Нюанс: http.DetectContentType распознаёт jpeg/png/webp, но не heic. Для HEIC проверяем ISO-BMFF бокс вручную: байты 4:8 == "ftyp" и бренд из набора heic/heix/hevc/mif1/....
Правило: любой пользовательский upload → whitelist типов + проверка magic-bytes + генерация имени на сервере (uuid+ext), никогда не доверять имени/типу от клиента.
Связанные заметки: [[MOC-security-patterns]], [[backend-validation]]
Источник: Code review 2026-06-30 (neyrogovnarik), security-fix в handlers/upload.go
#golang #backend #security #file-upload #best-practice