python-magic:拡張子ではなくファイル内容で判定する最も実用的な方法

サーバーに画像アップロード機能を追加すると、いつかこうした要件が出てきます。

  • .png でアップロードされたのに…本当に PNG ですか?」
  • 「このファイルが画像か文書かを先に分岐させる必要があります」
  • 「外部パーサー(Pillow/OpenCV)を使う前に、少なくともタイプを確認したい」

このとき最も確実な出発点は 拡張子 ではなく ファイル内容 です。 そして「内容ベースで判定」するのを最も手軽に実現できるツールが python-magic です。


python-magic は何をしてくれるのか

python-magiclibmagic という C ライブラリを Python から利用できるようにするラッパーです。libmagic はファイルの ヘッダー(最初の数バイト) などの特徴を調べてファイルタイプを識別します。この機能は Unix の file コマンドでも提供されています。

まとめると:

  • file(Linux コマンド) = 「ターミナルで使うインターフェース」
  • libmagic = 「コアエンジン(判定ロジック)」
  • python-magic = 「Python から libmagic を呼び出す薄いラッパー」

この記事では python-magic を中心に、このエンジンが実際にファイルタイプをどう判定するか を構造的に説明します。


コアエンジン(libmagic)はどう動くのか

libmagic ライブラリの動作原理図

libmagic の正体はシンプルです。

「ファイルタイプ判定規則が集まった データベース を読み込み、 その規則に従ってファイルバイトを検査し、最も妥当な結論を導き出す」

ここでいうデータベースが magic database(マジックパターン DB)で、file/libmagic が共に利用します。通常はシステムにコンパイルされた形(magic.mgc)でインストールされています。

1) 「マジックファイル」は規則の集合

この規則は基本的に次の要素で構成されます。

  • どこを見るか(offset:ファイルの何バイト位置)
  • どのように読むか(type:バイト/文字列/整数など)
  • 何と比較するか(expected value/pattern)
  • どんな結論を出すか(message/MIME など)

マニュアルでも file が「magic patterns」を調べると説明されています。 また、これらの規則は一行ずつテストされ(オフセット/タイプ/値/メッセージ)、条件が合えばより具体的なサブテストへと下位階層化される「階層構造」で組織されています。

Linux の file コマンドについて詳しく知りたい方は、以下のリンクをクリックしてください。

Linux file コマンドを調べる

2) 規則 DB は「テキスト原稿」と「コンパイル済み結果」の両方がある

マジック DB は元々人が読むテキストの断片の集合であり、性能向上のためにコンパイル済みバイナリ 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 はあくまで 推定/識別 ツールです。 セキュリティ的に完全な判定(悪意あるペイロード除外など)が目的なら、追加検証(ホワイトリスト、サイズ制限、サンドボックスデコード等)が必要です。


まとめ:python-magic は「ファイルタイプ判定をコードで持ち込む最軽量な方法」

python-magic が提供するのは画像処理そのものではありません。 代わりに「このファイルを何として扱うべきか」を迅速に教えてくれます。

  • エンジンは file と同じ libmagic
  • 判定方式は「規則 DB + バイト検査」
  • 開発現場ではアップロード検証、ルーティング、コスト削減に特に有用

これを習得すれば、ライブラリが不足している環境でも「判定 → 分岐 → 安全装置」を構築できます。


次回予告

  • Pillow(PIL)の open()load()verify() がそれぞれ何を保証し、いつどのメソッドを使うべきか、どのように動作するかを整理します。

関連記事