DevToolBox

MD5とSHA-256の違い - パスワード保存にどちらも使えない理由と正解

「MD5は古いからSHA-256にすれば安全」 - 半分正解で、半分は危険な誤解です。署名やチェックサムの用途ではその通りですが、パスワード保存では SHA-256 も不正解。速いハッシュ関数はGPUによる総当たりに 無力だからです。本記事では衝突攻撃の現状を整理した上で、SHA-256 の正しい使いどころと、 パスワード保存の正解(bcrypt / scrypt / Argon2id)のコスト設定までを実装つきで解説します。

MD5 is broken for signatures because collisions now take seconds to generate, while SHA-256 remains the standard for checksums, signatures, and HMAC. Neither is suitable for password storage, though: both are so fast that GPUs brute-force leaked hashes in days. Use Argon2id, scrypt, or bcrypt instead.

TL;DR

Hash Generator

MD5・SHA-1・SHA-256のハッシュ値をブラウザ内で即生成。同じ入力でアルゴリズムごとの出力長の違いを見比べられます。入力データは外部送信されません。

今すぐ試す →

1. MD5とSHA-256の基本的な違い / MD5 vs SHA-256 at a glance

どちらも任意長の入力から固定長の値を計算する暗号学的ハッシュ関数ですが、 設計年代も安全性の評価もまったく異なります。

項目 / ItemMD5SHA-256
規格 / SpecRFC 1321(1992年)FIPS 180-4(SHA-2ファミリー、2001年〜)
出力長 / Digest size128ビット(16進32文字)256ビット(16進64文字)
衝突攻撃 / Collisions実用化済み(数秒で生成)実用的な攻撃なし
適切な用途 / Valid uses非セキュリティの破損検出程度チェックサム・署名・HMAC
パスワード保存 / Passwords不適(速すぎる)不適(速すぎる)

同じ入力 password に対する出力はこう変わります。

MD5     : 5f4dcc3b5aa765d61d8327deb882cf99
SHA-256 : 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8

MD5 (1992, 128-bit) has practical collision attacks; SHA-256 (SHA-2 family, 256-bit) does not. Neither qualifies for password storage - that failure is about speed, not collisions.

2. 衝突攻撃の現状 - MD5とSHA-1はなぜ「壊れた」のか / Where collision attacks stand

衝突攻撃とは「同じハッシュ値を持つ異なる2つの入力」を作る攻撃です。 MD5は2004年に Wang らが実用的な衝突生成を実証し、現在はノートPCで数秒で作れます。 実害も既に出ており、2008年にはMD5署名の中間CA証明書の偽造が実証され、2012年のマルウェア Flame は選択プレフィックス衝突で Microsoft のコード署名を偽装しました。

SHA-1も2017年の SHAttered(Google/CWI、約263回の計算で衝突PDFを公開)で陥落し、 2020年には選択プレフィックス衝突(SHA-1 is a Shambles)まで実証されています。 主要ブラウザはSHA-1のTLS証明書を2017年に拒否済みです。

ここで重要な区別がひとつ。衝突攻撃が壊すのは署名・証明書・同一性の保証です。 ハッシュ値から元の入力を逆算する原像攻撃はMD5でも実用化されていません。 つまり「パスワード保存にMD5/SHA-1がダメな理由」は衝突ではなく、 次章以降で見る計算の速さにあります。

MD5 collisions have been practical since 2004 (rogue CA certs in 2008, the Flame malware in 2012), and SHA-1 fell with SHAttered in 2017 and chosen-prefix collisions in 2020. Collisions break signatures and certificates; password storage fails for a different reason - raw speed.

3. SHA-256の正しい用途 - チェックサム・署名・HMAC / What SHA-256 is actually for

SHA-256に実用的な攻撃は知られておらず、「速くて衝突しない」ことが価値になる用途では現行標準です。

# Linux / macOS
sha256sum node-v22.16.0.tar.gz

# Windows (PowerShell)
Get-FileHash node-v22.16.0.zip -Algorithm SHA256

皮肉なことに、これらの用途でSHA-256を優秀にしている「1秒に何十億回も計算できる速さ」こそが、 パスワード保存では致命傷になります。

SHA-256 is the right tool wherever fast, collision-free hashing is the goal: file checksums, TLS and code-signing certificates, HMAC/JWT signatures, and content addressing. The very speed that makes it great there is exactly what disqualifies it for passwords.

4. パスワード保存に高速ハッシュが不適な理由 / Why fast hashes fail for passwords

想定すべき脅威は「DBが漏えいし、攻撃者が手元のGPUでオフライン総当たりする」状況です。 公開されているhashcatのベンチマーク(RTX 4090 1枚)では、速度はおおよそ次のオーダーになります。

アルゴリズム / Algorithm試行速度(GPU 1枚)/ Speed8文字フルランダム(95種・約6,600兆通り)の全探索
MD5約1,640億回/秒約11時間
SHA-1約510億回/秒約1.5日
SHA-256約220億回/秒約3.5日
bcrypt(cost 12)約1,400回/秒理論値で約15万年

実際の攻撃は総当たりではなく、漏えいパスワード辞書+変形ルールが主流なので体感はさらに速くなります。 ありがちな誤解が「ソルトを付ければSHA-256でも安全」というもの。 ソルト(ユーザーごとのランダム値)が防ぐのはレインボーテーブル(事前計算表)と 同一パスワードの検出であって、1件ずつの試行速度は1ミリも変わりません。

// NG: 高速ハッシュの単純適用(ソルトを連結しても速度問題は残る)
const crypto = require("crypto");
const hash = crypto.createHash("sha256").update(password).digest("hex");

Threat model: an attacker brute-forces a leaked database offline. One GPU tries roughly 164 billion MD5 or 22 billion SHA-256 candidates per second, so an 8-character random password falls in hours to days. A salt only defeats rainbow tables - it does not slow down a single guess at all.

5. 正解: bcrypt / scrypt / Argon2id の選定とコスト設定 / Choosing and tuning a password hash

パスワード専用ハッシュは意図的に遅く、メモリを大量に使うよう設計されており、 GPU・ASICによる並列化を割に合わなくします。選定の目安は次の通りです。

アルゴリズム / AlgorithmOWASP推奨の最低ライン / OWASP minimum
Argon2idメモリ19MiB・反復2回・並列度1(m=19456, t=2, p=1)
scryptN=217, r=8, p=1
bcryptcost 10以上(入力は先頭72バイトまでの制限に注意)
PBKDF2-HMAC-SHA256600,000回以上

Node.jsでの実装例。ソルトは自動生成され、パラメータごと出力文字列に埋め込まれるため自前管理は不要です。

// Argon2id(npm: argon2)
const argon2 = require("argon2");
const hash = await argon2.hash(password, {
  type: argon2.argon2id,
  memoryCost: 19456, // KiB = 19 MiB
  timeCost: 2,
  parallelism: 1,
});
// => $argon2id$v=19$m=19456,t=2,p=1$<ソルト>$<ハッシュ>
const ok = await argon2.verify(hash, password);

// bcrypt(npm: bcrypt)
const bcrypt = require("bcrypt");
const hash2 = await bcrypt.hash(password, 12); // cost=12
// => $2b$12$<22文字のソルト><31文字のハッシュ>
const ok2 = await bcrypt.compare(password, hash2);

コストは「自社サーバーで認証1回あたり数百ミリ秒以内」を上限に、許せる範囲で高く設定します。 さらに防御を足すならペッパー: 全ユーザー共通の秘密値をDBの外(環境変数・KMS)に置き、HMAC-SHA256(pepper, password) を計算してからArgon2idに通します。 DB単体の漏えいではオフライン攻撃自体が成立しなくなります。 パスワード自体の強度確保にはPassword Generatorも活用してください。

Use Argon2id (OWASP minimum: 19 MiB memory, 2 iterations, parallelism 1), scrypt, or bcrypt (cost 10+, mind the 72-byte input limit). Libraries embed the salt and parameters in the output string. Add a pepper - a server-side secret kept outside the database - via HMAC before hashing.

English summary

MD5 (RFC 1321, 1992) and SHA-256 (FIPS 180-4, SHA-2 family) are both cryptographic hash functions, but their security status could not differ more. Practical MD5 collisions were demonstrated by Wang et al. in 2004 and now take seconds on a laptop; real-world exploits include a rogue CA certificate in 2008 and the Flame malware forging Microsoft code signatures in 2012. SHA-1 followed with the SHAttered collision in 2017 and chosen-prefix collisions in 2020, so browsers rejected SHA-1 TLS certificates back in 2017. SHA-256 has no practical collision or preimage attacks and remains the standard for file checksums, digital signatures, HMAC, JWTs, and content addressing. None of this, however, makes SHA-256 a password hash. The threat against stored passwords is offline brute force against a leaked database, and speed is the enemy: a single consumer GPU computes roughly 164 billion MD5 hashes or 22 billion SHA-256 hashes per second, cracking an 8-character random password within hours to days. A per-user salt defeats rainbow tables and duplicate detection but does not slow down guessing. The correct tools are dedicated, deliberately slow and memory-hard functions: Argon2id (recommended by RFC 9106; OWASP minimum m=19456 KiB, t=2, p=1), scrypt (N=2^17, r=8, p=1), bcrypt (cost 10 or higher, with its 72-byte input truncation in mind), or PBKDF2-HMAC-SHA256 with at least 600,000 iterations where FIPS compliance is required. Modern libraries generate the salt and embed all parameters in the output string. For defense in depth, add a pepper - a server-side secret stored outside the database in an env var or KMS - by HMAC-ing the password before hashing.

まとめ

Hash Generator

記事中のMD5・SHA-256の出力例を実際に生成して確認できます。ダウンロードファイルのチェックサム照合にもどうぞ。計算はすべてブラウザ内で完結します。

今すぐ試す →

関連ツール / Related tools

関連ガイド / Related guides