コンテンツへスキップ

ハッシュ関数とチェックサムの基礎 — ファイルの破損や書き換えをどう見つけるのか

ハッシュ関数とチェックサムの基礎 — ファイルの破損や書き換えをどう見つけるのか

ダウンロードしたファイルの横に、sha256: 8b99fa28… のような長い文字列が載っていることがある。バックアップを取ったあと、「このファイルは壊れていないか」を確かめたい場面もある。こうした場面で使われるのがハッシュ関数とチェックサムである。

この記事では、両者が何をしているのかを、Python 標準ライブラリでの小さな実験で確かめながら整理する。なお、暗号や認証の細かな実装には踏み込まず、「ファイルが同じかどうかを見分ける」という一般的な用途に絞って書く。

補足: 数値は、2026年10月13日に手元の Python 3 と標準ライブラリ(hashlib / zlib)で実行した結果。特定の製品やサービスの実測ではない。

ハッシュ関数とは何か

ハッシュ関数は、任意の長さのデータを入力すると、決まった長さの短い値(ハッシュ値、ダイジェスト)を返す関数である。

性質は次の3点に整理できる。

  1. 同じ入力からは、いつも同じ値が出る
  2. 入力が少しでも違うと、まったく違う値になる
  3. 値から元のデータを復元することは、現実的にはできない

1つ目は、「同じファイルなら同じ値になる」ことを意味する。ファイルの比較に使えるのはこのためである。

1文字違うだけでどう変わるか

次の2つの文字列は、最後の1文字(! と ?)だけが違う。

  • Hello, checksum!
  • Hello, checksum?

SHA-256 の結果は次のとおりだった。

入力 SHA-256(16進で64文字)
Hello, checksum! 8b99fa28a4076b2b…084221f
Hello, checksum? d4469d31724debb7…1690eed

先頭から見て、似ている部分がまったくない。2つの値を2進数にして比べると、256ビット中139ビットが違っていた。ほぼ半分が入れ替わっている計算で、入力が「近い」ことは値から読み取れない。

一方、入力が空(0バイト)でも値は出る。SHA-256 の場合は常に64文字で、空データの値は e3b0c442…b855 と決まっている。入力が1文字でも10MBでも、出力の長さは変わらない。

10MBの中の1ビットを検出できるか

もう少し現実に近い実験をした。ランダムな10MB(10,485,760バイト)のデータを作り、真ん中あたりの1ビットだけを反転させて、前後の値を比べた。

方式 反転前 反転後
SHA-256(先頭16文字) 5f8058d32a23c184 e9ec8934165d9732
CRC32 0x27327ccb 0xea770c2f

どちらの方式でも、10MBのうちたった1ビットの違いが、値の違いとして現れた。ファイルが「ほぼ同じ」でも、1ビットでも違えば値は変わるので、破損の検出には十分に使える。

なお、SHA-256 で10MBを処理する時間は、今回の環境で約0.004秒だった。大きなファイルでも、読み込みの時間のほうが支配的になりやすい。

チェックサムとの違い

「ハッシュ」と「チェックサム」は、現場ではほぼ同じ意味で使われることも多い。ただ、狙いで分けると次のように整理できる。

呼び方 主な狙い 代表例
チェックサム 偶然の破損(通信エラー、書き込み失敗)を、軽い計算で見つける CRC32、Adler-32
暗号学的ハッシュ 偶然の破損に加えて、意図的な書き換えにも耐えるように設計されている SHA-256、SHA-512

CRC32 は計算が軽く、値も32ビット(16進で8文字)と短い。通信やアーカイブ形式の中で、うっかりした破損を見つけるために広く使われている。

ただし、値が短いぶん、違うデータが同じ値になる可能性は暗号学的ハッシュより高い。また、CRC32 は、悪意のある人が値を合わせたまま内容だけ書き換えることを防ぐ設計ではない。

この違いを踏まえると、使い分けは次のようになる。

  • 偶然の破損を見つけたい(バックアップのコピーが壊れていないか等): CRC32 でも用は足りるが、SHA-256 でも負担は小さい
  • 配布されたファイルが、公開されたものと同じか確かめたい: SHA-256 以上を使う

MD5 や SHA-1 も標準ライブラリにはあるが、意図的な書き換えへの耐性を期待する用途では、現在は推奨されない。新しく作るものでは、SHA-256 以上を選んでおくのが無難である。

ファイルを検証するときの基本の流れ

ファイルの整合性を確かめる基本の流れは、次の3段階である。

  1. 正しい状態で、ハッシュ値を記録する(配布元が公開している値、またはバックアップ時に自分で記録した値)
  2. 確かめたいときに、同じ方式でハッシュ値を計算し直す
  3. 2つの値を比べる。一致すれば同じ内容、違えば何かが変わっている

Python で、ファイルを一定量ずつ読んで計算する例を示す。大きなファイルでも、メモリを使い切らないようにするためである。

import hashlib

def file_sha256(path, chunk_size=1024 * 1024):
    h = hashlib.sha256()
    with open(path, "rb") as f:
        while chunk := f.read(chunk_size):
            h.update(chunk)
    return h.hexdigest()

コマンドラインでは、macOS なら shasum -a 256 ファイル名、Linux なら sha256sum ファイル名 で同じ値が得られる。

気をつけたい4つの点

1. 「一致」は、記録した値が正しいことが前提

ハッシュ値の比較で分かるのは、記録した時点の内容と同じかどうかだけである。ファイルと一緒に、同じ場所に置いてあるハッシュ値は、ファイルとまとめて書き換えられていれば意味を持たない。値は、ファイルとは別の経路で入手・保管するほうが、検証としての意味が高まる。

2. 比べる前に、方式をそろえる

SHA-256 の値と SHA-1 の値は、長さも中身も違う。値を見比べるときは、どの方式で計算したものかを必ず確認する。値の横に方式名を書いておくと、あとで迷わない。

3. テキストの改行や文字コードで、値は変わる

見た目が同じテキストでも、改行コード(LF と CRLF)や文字コードが違えば、ハッシュ値は変わる。ハッシュ値が計算されるのは、画面に見える文字ではなく、実際のバイト列だからである。「同じファイルのはずなのに値が違う」ときは、まずここを疑う。

4. ハッシュ値は、パスワードの保管方法の答えではない

ハッシュ関数は、パスワードの保存にも使われる。ただし、その場合は、ファイルの検証とは別の要件(わざと遅く計算する、ソルトを加える等)が必要になる。ファイル検証用の SHA-256 を、そのままパスワードの保管に流用してはいけない。ここでは、用途が違うことだけ押さえておけばよい。

まとめ

  • ハッシュ関数は、任意の長さのデータから、決まった長さの値を作る。同じ入力からは同じ値、入力が1ビット違えば別の値になる
  • チェックサム(CRC32 等)は偶然の破損の検出、暗号学的ハッシュ(SHA-256 等)は意図的な書き換えも想定した設計と整理できる
  • 10MB のデータで1ビットを反転させる実験では、SHA-256・CRC32 のどちらでも値の違いとして検出できた
  • 検証では、記録した値が正しいこと・方式をそろえること・実際のバイト列を比べていることの3点を確認しておく

ファイルをコピーしたり、バックアップを取ったり、何かを配布したりするときは、「同じものができているか」を値で確かめる習慣があると、あとから原因を探す手間が減る。同じ「確認」を扱った記事として、冪等性(idempotency)とはも参照してほしい。