HOWTO · Python
Python チェックサム
Python の hashlib でファイルのチェックサムを生成し、信頼できる SHA-256 ダイジェストと比較する方法を解説します。
このページの内容
ファイルのチェックサムは、ファイルのバイト列から計算される短いダイジェストです。ファイルをダウンロードした後にチェックサムを計算し、信頼できる経路で公開されたダイジェストと比較できます。一致すれば、偶発的な変更や破損の検出に役立ちますが、それだけでファイルの作成者を証明することはできません。ファイルと参照用ダイジェストの両方を置き換えられる攻撃者なら、両者を一致させられます。
Python の hashlib モジュールには、標準的なハッシュアルゴリズムが用意されています。同じバイト列とアルゴリズムからは同じダイジェストが得られ、通常は1バイト変わるだけでも結果が変わります。チェックサムが表すのはバイト列であり、ファイル名やファイル形式ではありません。
ファイルのチェックサムでわかること
チェックサムは、コピーしたファイルの内容が期待どおりかどうかを確認するのに役立ちます。期待値は、別途認証されたリリースページなど、信頼できる情報源から取得してください。信頼できない同じ場所からダウンロードした2つの値を比較しても、ほとんど保証にはなりません。セキュリティ上の判断には、SHA-256 のような最新の暗号学的ハッシュを使い、参照値の入手元も別途確認してください。
hashlib でチェックサムを生成する
Python がテキストをデコードしたり改行を変換したりせず、ファイルの正確なバイト列をハッシュできるよう、ファイルはバイナリモードで開きます。データをチャンク単位で読み、各チャンクをハッシュオブジェクトの update() メソッドに渡します。これにより、利用可能なメモリを大幅に超えるファイルでもメモリ使用量を抑えられます。hexdigest() は印字可能な16進テキストを返し、digest() は同じダイジェストをパックされたバイト列で返します。
次の例を実行してください。
このセクションのコードと表示されるラベルや出力は英語のままです。
import hashlib
from pathlib import Path
from tempfile import TemporaryDirectory
def sha256_file(path):
digest = hashlib.sha256()
with open(path, "rb") as file:
for chunk in iter(lambda: file.read(64 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
with TemporaryDirectory() as directory:
path = Path(directory) / "sample.bin"
path.write_bytes(b"abc")
expected = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
actual = sha256_file(path)
print(f"SHA-256: {actual}")
print(f"Matches trusted value: {actual == expected}")
SHA-256: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
Matches trusted value: True
コード例と表示される出力は英語のままです。コードは、そのまま実行できるよう小さなサンプルファイルを作成します。自分のスクリプトでは、path をダウンロードしたファイルのパスに置き換えてください。ダウンロードしたファイルを使う場合は、path にその保存場所を指定し、得られたダイジェストをそのファイルの信頼できる参照値と比較します。
信頼できるチェックサムとファイルを照合する
期待するダイジェストは別途保存するか取得し、sha256_file の戻り値と比較します。例の真偽値による比較は、2つの16進文字列が一致すれば True になります。False の場合、ファイルは参照値と一致していません。破損した、意図的に変更された、または誤った参照値と組み合わされた可能性があります。不一致だけで特定の原因がわかったと判断せず、再ダウンロードするか入手元を調べてください。
セキュリティが重要な用途では、公開元が指定する形式に従って期待値の文字列を正規化し、ダイジェスト全体を比較してください。完全なダイジェストがある場合、短い接頭部分だけを比較してはいけません。比較に成功すれば、信頼できる期待値が表すバイト列との対応を確認できますが、参照値自体が認証されていない限り、公開元を認証することにはなりません。
大きなファイルの扱いとよくある間違い
チャンク単位で読み込めば、ファイル全体をメモリに読み込まずに済みます。すべてのバイトを一度ずつ順番に読み込む限り、チャンクのサイズは入出力の効率には影響しますが、得られるダイジェストには影響しません。テキストモードではデータがデコードされたり改行が変換されたりすることがあるため、バイナリモードを使うことが重要です。with 文を使い、読み込み中だけファイルを開いておきます。
非常に大きなファイルでは、file.read() ですべてのバイトを一度に読み込まないでください。また、パスが照合したいファイルを指していることと、参照値の計算に使われたものと同じハッシュアルゴリズムをローカルでも使っていることを確認してください。ファイルの内容が変わっていなくても、文字列の誤記やアルゴリズムの違いで値は一致しません。計算中にファイルが変更されると、内容が混在した状態のダイジェストになる可能性があります。別のプロセスが書き込み中かもしれない場合は、変更されないコピーを使って確認してください。
ハッシュアルゴリズムの選び方とその限界
汎用的なファイルダイジェストには、信頼できる公開元が SHA-256 の参照値を提供している場合、SHA-256 を使います。アルゴリズム名は hashlib.sha256() から利用できます。MD5 は古い例や互換性を保つ処理に残っていることがありますが、衝突攻撃に弱く、セキュリティが重要な整合性確認には適しません。真正性の確認や意図的な攻撃者からの保護に MD5 を使わないでください。
チェックサムとパスワードハッシュも異なります。ファイルの比較には高速なダイジェストが適していますが、パスワードには専用のソルト付きパスワードハッシュ方式が必要です。いずれの場合も、ダイジェストで確認できるのはバイト列が参照値に対応しているかどうかだけです。信頼性は、その参照値をどのように入手し、保護したかによって決まります。