MD5 vs SHA-1 vs SHA-256: ハッシュアルゴリズムの比較
· 12分で読めます
目次
ハッシュ化の詳細な理解
ハッシュ関数は安全なコンピューティングの基盤を形成し、任意の入力データをハッシュまたはダイジェストと呼ばれる固定サイズの文字列に変換します。この暗号化プロセスは基本的に一方向です。ハッシュ出力だけから元の入力をリバースエンジニアリングすることはできません。
この不可逆性により、ハッシュ化はデータ整合性の検証、デジタル署名の生成、パスワードストレージの保護、分散システムにおけるデータブロックの一意識別子の作成などのアプリケーションに非常に有用です。
実用的なシナリオを考えてみましょう。インターネットからソフトウェアをダウンロードする際、プロバイダーはダウンロードリンクと一緒にMD5またはSHA-256ハッシュを含めることがよくあります。ダウンロード後、ファイルをローカルでハッシュ化し、結果を公開されたハッシュと比較できます。一致すれば、転送中にファイルが破損または改ざんされていないことを確認できます。
プロのヒント: 当社のハッシュ計算機を使用して、コードを書かずに任意のテキストやファイルのMD5、SHA-1、SHA-256ハッシュを即座に生成・比較できます。
ハッシュには、セキュリティアプリケーションに有用ないくつかの重要な特性があります:
- 決定論的: 同じ入力は常に同じハッシュ出力を生成します
- 固定サイズ: 入力の長さに関係なく、ハッシュは常に同じサイズです
- 雪崩効果: 入力のわずかな変更で完全に異なるハッシュが生成されます
- 原像耐性: ハッシュを逆算して元の入力を見つけることは計算上不可能です
- 衝突耐性: 同じハッシュを生成する2つの異なる入力を見つけることは極めて困難です
ハッシュ関数の仕組み
ハッシュ関数は本質的に、複数ラウンドの操作を通じて入力データに数学的変換を適用します。これらの操作には通常、ビット演算、モジュラー演算、データを複雑で不可逆的な方法でスクランブルする論理関数が含まれます。
プロセスは一般的に次の手順に従います:
- パディング: 入力は特定の長さ要件を満たすようにパディングされます
- 解析: パディングされた入力は固定サイズのブロックに分割されます
- 処理: 各ブロックは圧縮関数を使用して複数ラウンドの変換を受けます
- 最終化: 最終状態がハッシュ出力に変換されます
雪崩効果はセキュリティにとって特に重要です。入力の1ビットでも変更すると、出力ハッシュのビットの約半分が変更されるはずです。この特性により、類似した入力が類似したハッシュを生成しないことが保証され、攻撃者が元のデータについて推測することを防ぎます。
「優れたハッシュ関数はランダムオラクルと区別がつかないものであるべきです。つまり、入力とは完全にランダムで無相関に見える出力を生成します。」— ブルース・シュナイアー、応用暗号理論
MD5: 機能、制限、および現代の使用例
MD5(メッセージダイジェストアルゴリズム5)は、1991年にロナルド・リベストによってMD4の改良版として設計されました。128ビット(16バイト)のハッシュ値を生成し、通常は32文字の16進数として表されます。
MD5はその速度とシンプルさから広く採用されました。長年にわたり、チェックサム、パスワードハッシュ化、デジタル署名の定番アルゴリズムでした。しかし、時間の経過とともに発見された暗号学的弱点により、セキュリティが重要でないアプリケーションに限定されるようになりました。
技術仕様
- 出力サイズ: 128ビット(32文字の16進数)
- ブロックサイズ: 512ビット
- ラウンド数: 4ラウンドにわたる64回の操作
- 速度: 非常に高速、最新ハードウェアで約400-500 MB/秒
コード実装
PythonでMD5ハッシュを生成する方法は次のとおりです:
import hashlib
def get_md5_hash(input_data):
"""入力文字列からMD5ハッシュを生成"""
return hashlib.md5(input_data.encode()).hexdigest()
# 使用例
text = "hash this string"
hash_result = get_md5_hash(text)
print(f"MD5: {hash_result}")
# 出力: c13b0a8f21c9b3a0b49c3cb482dd82b4
# ファイルのハッシュ化
def hash_file_md5(filename):
"""ファイルのMD5ハッシュを生成"""
md5_hash = hashlib.md5()
with open(filename, "rb") as f:
# 大きなファイルを処理するためにチャンクで読み込む
for chunk in iter(lambda: f.read(4096), b""):
md5_hash.update(chunk)
return md5_hash.hexdigest()
セキュリティ脆弱性
MD5の主な弱点は、衝突攻撃に対する脆弱性です。2004年、研究者は実用的な衝突攻撃を実証し、同一のMD5ハッシュを生成する2つの異なる入力を作成できることを示しました。2008年までに、攻撃者はMD5衝突を使用して不正なSSL証明書を作成しました。
その影響は深刻です。攻撃者が正規のファイルと同じMD5ハッシュを持つ悪意のあるファイルを作成できれば、検出されずに一方を他方に置き換えることができます。これにより、MD5はセキュリティに敏感なアプリケーションには不適切になります。
MD5を使用すべき場合
暗号学的弱点にもかかわらず、MD5は非セキュリティ目的には依然として有用です:
- チェックサム: ダウンロード中のファイル整合性の検証(セキュリティが重要でない場合)
- 重複排除: バックアップシステムでの重複ファイルの識別
- キャッシュキー: キャッシュされたデータの一意識別子の生成
- 非暗号化識別子: 衝突耐性が重要でない場合の一意IDの作成
クイックヒント: パスワードハッシュ化、デジタル署名、またはセキュリティが重要なアプリケーションにはMD5を使用しないでください。代わりにSHA-256またはbcryptを使用してください。
SHA-1: 進化と現状
SHA-1(セキュアハッシュアルゴリズム1)は、NSAによって開発され、1995年にNISTによって公開されました。160ビット(20バイト)のハッシュ値を生成し、より大きな出力サイズによりMD5よりも高いセキュリティを提供します。
SHA-1は、SSL証明書、Gitバージョン管理、デジタル署名など、多くのセキュリティアプリケーションの標準となりました。しかし、MD5と同様に、理論的な脆弱性が最終的に実用的な攻撃となりました。
技術仕様
- 出力サイズ: 160ビット(40文字の16進数)
- ブロックサイズ: 512ビット
- ラウンド数: 80回の操作
- 速度: 高速、最新ハードウェアで約300-400 MB/秒
コード実装
import hashlib
def get_sha1_hash(input_data):
"""入力文字列からSHA-1ハッシュを生成"""
return hashlib.sha1(input_data.encode()).hexdigest()
# 使用例
text = "hash this string"
hash_result = get_sha1_hash(text)
print(f"SHA-1: {hash_result}")
# 出力: 3c3a3c22c0e8e8c8e8c8e8c8e8c8e8c8e8c8e8c8
# 複数のアルゴリズムの比較
def compare_hashes(text):
"""アルゴリズム間でハッシュ出力を比較"""
return {
'MD5': hashlib.md5(text.encode()).hexdigest(),
'SHA-1': hashlib.sha1(text.encode()).hexdigest(),
'SHA-256': hashlib.sha256(text.encode()).hexdigest()
}
results = compare_hashes("example")
for algo, hash_val in results.items():
print(f"{algo}: {hash_val}")
SHAttered攻撃
2017年2月、GoogleはSHAtteredと呼ばれる最初の実用的なSHA-1衝突攻撃を発表しました。研究者は同一のSHA-1ハッシュを生成する2つの異なるPDFファイルを作成し、SHA-1が実際には衝突耐性がないことを実証しました。
この攻撃には、約6,500 CPU年と110 GPU年という膨大な計算リソースが必要でしたが、SHA-1衝突が実現可能であることを証明しました。これにより、主要な組織はセキュリティが重要なアプリケーションでSHA-1を非推奨にしました。
現状と使用状況
主要なブラウザは2017年にSHA-1 SSL証明書の受け入れを停止しました。コミットハッシュに歴史的にSHA-1を使用していたGitは、SHA-256に移行しています。しかし、SHA-1はレガシーシステムや重要でないアプリケーションで依然として使用されています。
今日のSHA-1の許容される使用例には次のものがあります:
- レガシーシステムの互換性: SHA-1を必要とする古いシステムとのインターフェース時
- 非敵対的チェックサム: 攻撃者が懸念されない場合のデータ整合性の検証
- HMAC操作: SHA-1は一部のコンテキストでHMAC(鍵付きハッシュ化)に許容されます
SHA-256: 現代の標準
SHA-256はSHA-2ファミリーの一部で、NSAによって設計され、2001年に公開されました。256ビット(32バイト)のハッシュ値を生成し、現在、実用的な攻撃が知られていない暗号学的に安全なものと考えられています。
SHA-256は、ブロックチェーン技術からSSL/TLS証明書、パスワードハッシュ化(適切なソルト付き)、デジタル署名まで、セキュリティが重要なアプリケーションの業界標準となっています。
技術仕様
- 出力サイズ: 256ビット(64文字の16進数)
- ブロックサイズ: 512ビット
- ラウンド数: 64回の操作
- 速度: 中程度、最新ハードウェアで約150-200 MB/秒
- セキュリティレベル: 128ビットセキュリティ(破るには2^128回の操作)
コード実装
import hashlib
def get_sha256_hash(input_data):
"""入力文字列からSHA-256ハッシュを生成"""
return hashlib.sha256(input_data.encode()).hexdigest()
# 使用例
text = "hash this string"
hash_result = get_sha256_hash(text)
print(f"SHA-256: {hash_result}")
# 出力: 8e35c2cd3bf6641bdb0e2050b76932cbb2e6034a0ddacc1d9bea82a6ba57f7cf
# ソルト付きパスワードハッシュ化(基本例 - 本番環境ではbcryptを使用)
import os
def hash_password_sha256(password):
"""ランダムソルトでパスワードをハッシュ化"""
salt = os.urandom(32) # ランダムな32バイトのソルトを生成
pwd_hash = hashlib.sha256(salt + password.encode()).hexdigest()
return salt.hex() + pwd_hash
def verify_password_sha256(stored_hash, password):
"""保存されたハッシュに対してパスワードを検証"""
salt = bytes.fromhex(stored_hash[:64])
stored_pwd_hash = stored_hash[64:]
pwd_hash = hashlib.sha256(salt + password.encode()).hexdigest()
return pwd_hash == stored_pwd_hash
プロのヒント: SHA-256は安全ですが、特にパスワードハッシュ化には、ブルートフォース攻撃に対して遅く耐性があるように設計されたbcrypt、scrypt、またはArgon2などの専用アルゴリズムを使用してください。
SHA-256が安全な理由
SHA-256のセキュリティは、いくつかの要因から来ています:
- より大きな出力空間: 2^256の可能な出力により、ブルートフォースによる衝突の発見は計算上不可能です
- 複雑な操作: MD5やSHA-1よりも洗練された数学的操作
- 広範な分析: 数十年にわたる暗号解析で実用的な弱点は見つかっていません
- 量子耐性: 量子コンピュータは一部の暗号化を脅かしますが、SHA-256は比較的安全です(ただし、長期的なセキュリティにはSHA-384またはSHA-512が好まれる場合があります)
実世界のアプリケーション
SHA-256はインターネット全体の重要なインフラストラクチャを支えています:
- ビットコインとブロックチェーン: SHA-256はビットコインのプルーフオブワークシステムを保護します
- SSL/TLS証明書: 最新の証明書は署名にSHA-256を使用します
- コード署名: ソフトウェア発行者はアプリケーションに署名するためにSHA-256を使用します
- 文書検証: 法的および金融文書は整合性検証にSHA-256を使用します
- API認証: 多くのAPIはリクエスト署名のためにHMACでSHA-256を使用します
並列比較
これらのアルゴリズム間の違いを理解することで、プロジェクトに関する情報に基づいた決定を下すことができます。包括的な比較は次のとおりです:
| 機能 | MD5 | SHA-1 | SHA-256 |
|---|---|---|---|
| 出力サイズ | 128ビット | 160ビット | 256ビット |