python-magic: самый практичный способ доверять содержимому файла, а не расширению

Когда на сервере появляется функция загрузки изображений, рано или поздно возникают такие требования.

  • «Файл пришёл как .png, но действительно ли это PNG?»
  • «Нужно сначала определить, является ли файл изображением или документом.»
  • «Перед тем как вызвать внешние парсеры (Pillow/OpenCV), хотелось бы хотя бы проверить тип.»

В этом случае самым надёжным отправным пунктом является содержимое файла, а не его расширение. И самый простой способ получить «проверку по содержимому» — это python-magic.


Что делает python-magic



python-magic — это обёртка над C-библиотекой libmagic. libmagic анализирует заголовок (первые несколько байт) файла и определяет его тип. Та же функциональность доступна в Unix-команде file.

Итак:

  • file (команда Linux) = «интерфейс в терминале»
  • libmagic = «ядро (логика определения)»
  • python-magic = «тонкая обёртка, позволяющая вызывать libmagic из Python»

В этой статье мы сосредоточимся на python-magic и разберём, как именно этот движок определяет тип файла.


Как работает ядро libmagic

Диаграмма работы библиотеки libmagic

Суть libmagic проста:

«Читает базу правил определения типа файла, проверяет байты файла согласно этим правилам и выводит наиболее вероятный результат.»

База правил — это magic database (magic pattern DB), которая обычно поставляется в скомпилированном виде (magic.mgc).

1) «Magic file» — набор правил

Правила состоят из:

  • Где смотреть (offset — позиция в файле)
  • Как читать (type — байт/строка/целое и т.д.)
  • С чем сравнивать (expected value/pattern)
  • Какой вывод (message/MIME и т.п.)

Каждое правило проверяется строка за строкой, и при совпадении переходит к более конкретным подправилам, образуя иерархию.

Для более подробного изучения команды file можно перейти по ссылке:

Изучаем команду file в Linux

2) База правил — текст и скомпилированный формат

Magic DB может быть как читаемым текстом, так и скомпилированным бинарным файлом (.mgc) для повышения производительности.

3) Итог: проверка содержимого вместо расширения

file всегда использует содержимое, а не расширение, чтобы определить тип. python-magic просто переносит эту философию в одну строку Python-кода.


Как использовать python-magic



Существует два основных паттерна использования.

1) Получить MIME-тип (самый практичный)

Полезно при обработке загрузок, маршрутизации, логировании и метриках.

import magic

mime = magic.from_file("upload.bin", mime=True)
print(mime)  # например: image/png

python-magic использует libmagic для определения типа файла, как и команда file.

2) Проверка по буферу (удобно для потоков загрузки)

Перед сохранением на диск часто хочется быстро проверить первые байты.

import magic

with open("upload.bin", "rb") as f:
    head = f.read(4096)

mime = magic.from_buffer(head, mime=True)
print(mime)

Буферная проверка особенно полезна как «первый фильтр» до сохранения файла.


Где это полезно с точки зрения разработчика

1) Первая линия защиты при проверке загрузок

  • Не полагаться только на расширение
  • Минимально проверить, можно ли обрабатывать файл как изображение

2) Точки ветвления в пайплайне обработки

  • Если изображение — перейти к ресайзу/миниатюрам
  • Если PDF/ZIP — отправить в другую очередь
  • Если тип неизвестен — изолировать/отклонить/дополнительно проверить

3) Сокращение затрат до вызова «тяжёлых декодеров»

Pillow и подобные декодеры мощны, но их вызов увеличивает потребление памяти, CPU и поверхность уязвимостей. python-magic позволяет заранее решить, стоит ли вызывать декодер.

Важный практический момент: libmagic — это только предположение/идентификация. Для полной безопасности (запрет вредоносных нагрузок) нужны дополнительные проверки (белый список, ограничение размера, sandbox-декодинг и т.д.).


Итог: python-magic — самый лёгкий способ получить определение типа файла в коде

python-magic не занимается обработкой изображений, а лишь быстро сообщает, как файл следует трактовать.

  • Движок — libmagic, как в file
  • Метод — «правила + проверка байтов»
  • Практика — проверка загрузок, маршрутизация, экономия ресурсов

Освоив его, вы сможете в любой среде, где недоступны полноценные библиотеки, построить цепочку «определение → ветвление → защита».


Предвкушение следующей статьи

  • Что гарантируют методы open(), load(), verify() в Pillow (PIL) и когда их использовать? Мы разберём их работу и назначение.

Смотрите также