DevKitLab Logo DevKitLab
ハッシュ / チェックサム / MD5 / SHA-256 / セキュリティ

MD5 と SHA-256、チェックサムにどちらを使うべきか

「MD5 は破られている、使うな」はよく聞く助言で、半分は正しく、だからこそ危険です。チェックサムに MD5 が十分か無謀かは、先に答えるべき一つの問いで決まります。誰かがあなたを欺こうとしているか、です。この記事はチェックサムの二つの仕事を切り分け、それぞれがどのハッシュを必要とするかを示し、なぜ「もっと強いハッシュ」がしばしば誤った対処なのかを説明します。

MD5 が併記されたダウンロードページを見ている。あるいは、自分のリリースの隣に何を載せるか決めようとしている。いずれにしても、浮かぶ問いは同じだ。MD5 はまだ使えるのか、それとも SHA-256 が必要なのか。検索すれば、声の大きな答えが一つ返ってくる――「MD5 は破られている、使うな」。この助言は半分だけ正しく、正しくないほうの半分が人を惑わせる。鵜呑みにすると、二つのうちどちらかが起きる。MD5 で十分だった場面で余計な手間をかけるか、あるいはもっと悪く、SHA-256 に替えて安心し、本当に必要だったものが強いハッシュなどではなかったことを見落とすかだ。

正直に言えばこうだ。「どのハッシュか」は「どのアルゴリズムが優れているか」という問いではなく、「誰から守るのか」という問いだ。人が公開されたハッシュを使うのは、まったく異なる二つの目的のためであり、MD5 は一方には申し分なく、他方にはまるで役に立たない。自分がどちらの目的を追っているのかが分かるまで、正しい答えは存在しない。

先に範囲を一つ断っておく。本稿が扱うのはファイルのチェックサム、つまりデータが変わっていないことの確認だ。パスワードの保存は別の問題で、答えも異なる。そちらでは Argon2id や bcrypt のような専用のパスワードハッシュを使うべきで、MD5 は論外、素の SHA-256 も適さない。

核心の一点:二つの仕事、一つの問い

公開されたハッシュを突き合わせるのは、次の二つの目的のどちらかを果たすためだ。この二つは別物で、裸のハッシュの働きも大きく異なる。

  1. 偶発的な破損を検出する。接続の切断がファイルを途中で切った。不安定なディスクが 1 ビットを反転させた。ミラーが古いコピーを返した。この差を設計した者はいない。ランダムな損傷であり、参照値をくれたソースは信頼している。これは事故に備える整合性であり、裸のハッシュだけで果たせる。
  2. 意図的な改ざんに抵抗する。悪意あるファイルを受け入れさせたい者が、そのハッシュを期待値に一致させようと能動的に動いている。これは真正性の問題であり、ここでは裸のハッシュは答えの一部にすぎない。欠かせない一層ではあるが、防御のすべてではない。この目的を果たすには、その上に署名(または HMAC)が要る。

MD5 か SHA-256 かという判断は、結局この一問に行き着く。攻撃者はいるか。目的 1 にはいない。だから MD5 で足りる。目的 2 にはいる。MD5 はそのまま崩れ、SHA-256 がハッシュ層での最低ラインになる。ただし目的 2 の要点を忘れないでほしい。ハッシュはその防御の一層であって、けっしてそのすべてではない。脅威モデルを正しく捉えれば、アルゴリズムはおのずと決まる。取り違えれば、要らない対策に手間をかけるか、どんな素のハッシュにもできないことをハッシュに期待することになる。

「MD5 は破られている」とは何を意味するか

この「破れ」には正確な名前があり、その正確さこそが核心だ。暗号学的ハッシュはいくつかの性質を備えるべきものだが、MD5 はそのうちちょうど一つを失い、残りは保っている。

  • 衝突耐性――異なる二つの入力同じハッシュを生む、その組を誰も見つけられない。MD5 が失ったのはこれだ。市販のツールで、いまやノートパソコン上でも MD5 衝突は作れる。(ただし、その二つのファイルに特定の、意味のある中身まで持たせる衝突――いわゆる chosen-prefix collision――はずっと難しく、どれだけ難しいかは攻撃の状況しだいだ。)SHA-1 も倒れた。2017 年の SHAttered 攻撃が、実際の SHA-1 衝突を作り出している。
  • 原像耐性と第二原像耐性――ハッシュが与えられても、それを生む入力を誰も構成できない(原像)。ある一つのファイルが与えられても、同じハッシュを持つ別のファイルを誰も見つけられない(第二原像)。この二つを MD5 は実用上まだ保っている既存の具体的な MD5 値、あるいは与えられたファイルを取り、それに一致するものを作り出す実行可能な攻撃は存在しない。

この区別こそ、MD5 がいまも仕事 1 で使える理由だ。偶発的な破損は、作り込まれた衝突ではない。切れたダウンロードや反転したビットは、変わってしまった一つのファイルであって、一致するように仕立てた二つのファイルではない。入力が変われば、MD5 の出力も確実に変わる。信頼できる公開者が事前に計算したハッシュでダウンロードを検証するのは置き換えの問題であり、頼っているのは原像耐性と第二原像耐性だ。この二つを MD5 は失っていない。だが、それで MD5 が認証に使えることにはならない。衝突耐性が破られており、現実の攻撃はまさにその扉から入ってくるからだ。

では、この衝突の破れは、攻撃者に実際に何をさせるのか。二つのファイルが必要で、そのどちらも攻撃者の手の内にある。提出されたどんなファイルにも署名するサービスを思い描いてほしい。攻撃者は、無害に見えるファイルと、それと MD5 が一致する「邪悪な双子」を作る。無害なほうを提出して署名を得たあと、双子のほうを配る。ハッシュが同じなので、署名はそのまま有効に見える。これは机上の話ではない。Flame マルウェアは MD5 衝突を用いて Microsoft のコード署名証明書を偽造した。研究者は同じ手口で不正な CA 証明書を作り出した。筋書きは常に同じだ。攻撃者は、悪いファイルだけでなく「良い」ファイルにも影響を及ぼせる必要がある。パイプラインがその隙を許した瞬間、衝突耐性を失ったハッシュは現実の脆弱性になる。しかも、それをふさぐのに費用はかからない。だから放置する理由はない。

まず全体の見取り図を一枚

細部に入る前に、名前を整理する表を一枚。

アルゴリズム出力衝突耐性こんなときに使う
CRC3232 ビットなし(本来の目的ではない)とにかく速さ。偶発的な誤りの検出。セキュリティは一切なし
MD5128 ビット破られている従来の非対抗チェック、重複排除、キャッシュキー
SHA-1160 ビット破られている新規には使わない。古いシステムとの互換のみ
SHA-256256 ビット実用的な攻撃は知られていない公開や署名を伴うワークフローの安全なハッシュ既定
SHA-512512 ビット実用的な攻撃は知られていない安全性は SHA-256 と同じ。64 ビットでは速いことも
BLAKE3256 ビット以上実用的な攻撃は知られていない最高速を求める新しいシステム

この記事の残りは、要するに三列目と四列目を正しく読むための話だ。

MD5 が本当に十分なとき

流行はいったん脇に置く。MD5 は、攻撃者がいないと考えてよい十分な根拠があり、なおかつ速度を重んじるか、すでに MD5 値が手元にある、というときには妥当な選択だ。

  • 信頼できるソースからの偶発的な破損を検出する。不安定なネットワークを越えてコピーした、あるいは自分の内部ミラーから引いたファイルが、無事に届いたかを確かめる。
  • 重複排除とキャッシュキー。二つのブロブが同一かどうかをハッシュで見分ける。あるいはキャッシュのキーにする。そのシステムの中で、意図的に衝突を起こそうとする者はいない。
  • 信頼できる公開者から受け取った MD5 を照合する。信頼するチャネルを通じて信頼するプロジェクトが MD5 しか公開していないなら、それを照合すれば、本来の目的だった偶発的な損傷は今も捕まえられる。無価値ではない。ただ、改ざんされたファイルへの防御ではない、というだけだ。

はっきり言っておきたい注意が一つ。ここでの「十分」は間違っていないという意味であって、新規の作業で推奨されるという意味ではない。しかも、偶発的な破損を素早く捕まえたいだけなら、CRC32 や xxHash のような非暗号的チェックサムのほうが多くの場合に向いている。その一つの仕事のために作られていて速く、あとで誤読されかねない「セキュリティ」という看板もない。

SHA-256(またはそれ以上)が必須のとき

攻撃者が触れうるものすべてに対する規則は単純だ。ハッシュ層では最低でも SHA-256。出会う機会は思っているより多い。SHA-256 は、日々使う多くのシステムの基盤になっている。

  • 改ざんされうる公開ダウンロード。ユーザーが開かれたインターネットから取得するもので、ミラーや中間者がファイルを差し替えられる場合すべて。
  • ソフトウェアのサプライチェーン。Docker イメージは SHA-256 ダイジェスト(sha256:…)で固定される。npm パッケージは Subresource Integrity ハッシュを記録するが、多くは base64 で書かれた SHA-512 だ。いずれも、差し替えられた成果物が検査に落ちるようにするためにある。
  • セキュリティに接する識別子。TLS 証明書のフィンガープリント、署名される内容、そして意図的な衝突に耐えるべきハッシュ由来の識別子。(秘密トークンは別の話で、ハッシュアルゴリズムの選択ではなく、暗号学的に安全な乱数から生成すべきだ。)
  • コンテンツアドレッシング。ここでは衝突は単なる厄介事ではなく、セキュリティ上のバグだ。Git が SHA-1 から移行しているのはこのためで、同じ ID を持つ二つの異なるコミットは、モデルの中核的な前提を壊してしまう。

新規の作業では、今日の用途が対抗的に見えなくても、既定を SHA-256 にする。脅威モデルは時とともに変わる。内部ツールが外に出る。「信頼できる」パイプラインに信頼できない入力が入り込む。そして SHA 命令を備えたプロセッサでは、MD5 に対する SHA-256 の追加コストはほぼゼロだ。新規プロジェクトで MD5 を選ぶ理由は、まず存在しない。

「長いハッシュのほうが安全では?」――ビット数と速度

二つの直感が、人を SHA-512 や「いちばん強いもの」へと向かわせる。どちらも正しておく価値がある。

第一に、長いことは、そのまま安全なことにはならない。MD5 や SHA-1 の問題は、衝突耐性の構造的な破れであって、ビット数が足りないことではない。出力のビットが増えれば、総当たり的な汎用の衝突探索の手間は確かに上がる。しかし SHA-256 はその点で最初から莫大な余裕を持っており、それを使い切ることはない。長い出力にできないのは、構造的な弱点を直すことと、真正性を与えることだ。SHA-256 に欠けているものを SHA-512 が補うわけではない。そもそも欠けていないからだ。

第二に、いちばん強いハッシュが最も遅いわけではなく、MD5 はもう最速ではない。現代の CPU にはハッシュ命令が組み込まれている。x86 の SHA-NI、ARM の暗号拡張だ。それらがあれば、SHA-256 は毎秒数ギガバイトで動き、しばしば MD5 に並ぶか上回る。BLAKE3 はさらに進み、SIMD と複数コアを活用する。選べるなかでは、たいてい最速の暗号学的ハッシュだ。純粋にスループットだけが欲しく、セキュリティは一切要らないのなら、ハードウェアの CRC32 がどれより速い。「MD5 は速い」は十年前には本物の論拠だった。今日のハードウェアでは、めったに成り立たない。

だから SHA-512 を選ぶなら、確かな理由のために選ぶ。長いダイジェストが欲しい、あるいは 64 ビットサーバーで速度を稼ぎたい、といった理由だ。得られもしないセキュリティの上乗せのためではない。

罠:強いハッシュは改ざん防止ではない

これは「MD5 を SHA-256 に替える」ことがひそかに助長する誤解なので、はっきり述べておく。アルゴリズムを上げること自体は、改ざんを防がない。これは二つの別々の軸であり、混同こそが「うちは SHA-256 だ」を偽りの安心に変えてしまう。

  • MD5 → SHA-256 で高まるのは衝突耐性だ。攻撃者が入力を作り込むときにだけ効く。
  • 素のハッシュ → 署名 で得られるのが真正性だ。攻撃者がファイルとその公開ハッシュの両方を書き換えられるとき、初めて意味を持つ。

ダウンロードとそのチェックサムが同じページにあるとしよう。攻撃者はそのページを支配している。ファイルを差し替え、ハッシュを一致するように書き換える。そのハッシュが MD5 でも SHA-512 でも、結果は変わらない。検査は通り、あなたは欺かれる。直し方は、より長いダイジェストではない。公開配布であれば、答えは署名だ。GPG や Ed25519、minisign を使い、公開者が秘密鍵で署名し、あなたが広く公開された公開鍵で検証する。こうなれば攻撃者は、鍵なしに参照値を偽造できない。HMAC も同じ問題を解けるが、それは双方がすでに共有秘密鍵を持つ閉じた環境――たとえば webhook や内部サービス間――に限られ、公開ダウンロードには使えない。Linux ディストリビューションが、単に強いハッシュではなく署名付きのチェックサムファイルを配るのは、まさにこのためだ。(二つの値がそもそも並ばないなら、それは別の問いだ。チェックサムが一致しないのはなぜでたどっている。)

一度で決める

順に答えていけば、アルゴリズムは自然と決まる。

  1. 攻撃者が入力を作り込むか、ファイルを差し替えられるか。できるなら、ハッシュ層は SHA-256 かそれ以上。例外はない。MD5 と SHA-1 は候補から外れる。
  2. これは公開ダウンロードか、セキュリティに接する何かか。それは問い 1 の「できる」として扱う。SHA-256 を公開し、そして署名する。改ざんされた参照値を退けるのは署名であって、ハッシュの長さではない。
  3. 純粋に偶発的な破損の検出で、攻撃者はおらず、ソースを掌握または信頼しているか。それなら MD5 で十分だ。もっとも、非暗号的チェックサム(CRC32、xxHash)のほうがたいてい向いていて速い。そして新しいものなら、どのみち SHA-256 が安全な既定になる。
  4. 他人が公開したファイルを検証しているのか。相手が公開に使ったアルゴリズムを使う。それが MD5 だけでも、ソースと経路を信頼できるなら、偶発的な整合性は今も確認できる。ただし、SHA-256 と署名の両方をくれる公開者を優先する。

ハッシュを自分で計算するだけなら、コマンドラインで足りる。Linux なら md5sum filesha256sum file。macOS はツール名が異なり、md5 -r fileshasum -a 256 file。Windows PowerShell なら Get-FileHash file -Algorithm SHA256 だ。ブラウザで手早く確かめたいなら、ファイルかテキストを ハッシュ生成ツール に渡す。計算は一度に一つのアルゴリズムなので、参照値が名指すもの(MD5、SHA-256、SHA-512、SHA-3、BLAKE2b/BLAKE2s/BLAKE3 など)を選び、アルゴリズムを切り替えて見比べる。出力は十六進であって、SRI で使われる Base64 ではない。だから integrity="sha256-…" のような値には、どちらか一方を変換する必要がある。

四つの問いの底にあるのは、冒頭のあの一点だ。これは脅威モデルの判断であって、順位付けではない。MD5 は普遍的に「破られている」わけでも、SHA-256 が普遍的に「答え」なわけでもない。MD5 が破られているのはただ一点、意図的な衝突に抵抗することにおいてだ。そしてそのただ一点こそ、セキュリティ用のチェックサムが必要とし、偶発的な破損の検査が必要としないものだ。まず、攻撃者がいるかどうかを決める。そして、いる場合には忘れないでほしい。ハッシュは基準線であって、防御のすべてではない。