atomic-file-upload-magic-bytes.md 2.1 KB

Проверять тип загружаемого файла по сигнатуре, а не по заголовку

Контекст: 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