
暗号学的ハッシュ関数の完全ガイド - SHA-256、SHA-512など
📷 Joppe Beurskens / Pexels暗号学的ハッシュ関数の完全ガイド - SHA-256、SHA-512など
SHA-256、SHA-512、MD5などの暗号学的ハッシュ関数を徹底解説。ハッシュの特性、チェックサム、パスワードハッシュ、ブロックチェーン、デジタル署名などの用途を紹介。
はじめに:ハッシュ関数とは?
ハッシュ関数は、任意のサイズの入力を受け取り、固定サイズの出力(ハッシュ、ダイジェスト、チェックサムとも呼ばれる)を生成する数学関数です。暗号学的ハッシュ関数には、ソフトウェア開発、サイバーセキュリティ、ブロックチェーン技術、データ整合性の検証に不可欠なセキュリティ特性が備わっています。
ハッシュ関数は現代のコンピューティングの至るところで使われています。ウェブサイトへのログイン、ファイルダウンロードの検証、Gitへのコードコミット、ブロックチェーンとのやり取り——そのすべての背後でハッシュ関数が動いています。ハッシュアルゴリズムの仕組みと使い分けを理解することは、すべての開発者にとって必須の知識です。
手軽にハッシュを生成したいですか?MD5、SHA-1、SHA-256、SHA-384、SHA-512に対応したハッシュジェネレーターをぜひお試しください。
暗号学的ハッシュ関数の基本特性
優れた暗号学的ハッシュ関数には、単純なチェックサムや通常のハッシュ関数と区別するいくつかの重要な特性が必要です。
1. 決定性
同じ入力は常に同じ出力を生成します。SHA-256で文字列「Hello, World!」をハッシュすると、どのマシン、どのプログラミング言語でも、毎回同じハッシュが得られます。
Input: "Hello, World!"
SHA-256: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
2. 固定長の出力
入力サイズに関係なく、出力は常に同じ長さです:
| アルゴリズム | 出力サイズ(ビット) | 出力サイズ(16進数文字) |
|---|---|---|
| MD5 | 128 | 32 |
| SHA-1 | 160 | 40 |
| SHA-224 | 224 | 56 |
| SHA-256 | 256 | 64 |
| SHA-384 | 384 | 96 |
| SHA-512 | 512 | 128 |
| SHA-3-256 | 256 | 64 |
| BLAKE3 | 256(デフォルト) | 64 |
Input: "a"
SHA-256: ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb
Input: "The quick brown fox jumps over the lazy dog"
SHA-256: d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592
Input: [1GBファイルの全内容]
SHA-256: [それでも正確に64文字の16進数]
3. 原像耐性
ハッシュ値から、そのハッシュを生成する入力を見つけることが計算上不可能であるべきです。つまり、ハッシュ関数は一方向関数であり、逆算できません。
Hash: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
入力を見つけられますか?総当たりでしかできません。
(ちなみに答えは空文字列です)
4. 第二原像耐性
ある入力とそのハッシュが与えられた場合、同じハッシュを生成する別の入力を見つけることが計算上不可能であるべきです。
5. 衝突耐性
同じハッシュ出力を生成する2つの異なる入力を見つけることが計算上不可能であるべきです。
6. アバランシェ効果
入力のわずかな変更が出力に劇的な変化をもたらすべきです。1ビットの変更でも、出力ビットのおよそ半分が変わるべきです。
Input: "Hello, World!"
SHA-256: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
Input: "Hello, World?" (!を?に変更)
SHA-256: 4a6e2ef4e4fa0e534f1d1989f453daa87f36e5e4e35b3dcb3d82f22d12654083
入力が1文字しか違わないのに、2つのハッシュは全く異なって見えます。ハッシュジェネレーターで実際に確認できます。
SHAファミリーのハッシュ関数
Secure Hash Algorithm(SHA)ファミリーは、最も広く使われている暗号学的ハッシュ関数群です。NSA(米国国家安全保障局)が開発し、NISTが公開しました。
SHA-1(非推奨)
SHA-1は160ビット(20バイト)のハッシュを生成します。長年標準でしたが、現在は破られたと見なされており、セキュリティ目的での使用は推奨されません。
- 状態:非推奨——新規アプリケーションには使用しないこと
- 衝突発見:2017年にGoogleとCWI Amsterdamが実用的な衝突を実証(SHAttered攻撃)
- まだ使用中:GitはオブジェクトIDにSHA-1を使用(SHA-256への移行中)
- 出力:40文字の16進数
SHA-256
SHA-256は現在最も広く使われているハッシュ関数です。SHA-2ファミリーの一部で、256ビット(32バイト)のハッシュを生成します。
- 状態:安全で広く推奨されている
- 用途:Bitcoin、TLS証明書、コード署名、ファイル整合性チェック
- 性能:中程度——ほとんどのアプリケーションに十分な速度
- 出力:64文字の16進数
// JavaScript: SHA-256ハッシュの生成
async function sha256(message) {
const encoder = new TextEncoder();
const data = encoder.encode(message);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
const hash = await sha256('Hello, World!');
console.log(hash);
// dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
SHA-384
SHA-384はSHA-512の切り詰めバージョンで、384ビットのハッシュを生成します。SHA-256よりもわずかに高いセキュリティマージンを提供します。
- 状態:安全
- 用途:TLS、高セキュリティアプリケーション
- 出力:96文字の16進数
SHA-512
SHA-512は512ビット(64バイト)のハッシュを生成します。64ビットシステムでは、より大きなブロックでデータを処理するため、実はSHA-256よりも高速です。
- 状態:安全
- 性能:64ビットシステムではSHA-256よりも高速なことが多い
- 用途:パスワードハッシュ、デジタル署名、高セキュリティアプリケーション
- 出力:128文字の16進数
import hashlib
# PythonでのSHA-512
message = "Hello, World!"
hash_object = hashlib.sha512(message.encode('utf-8'))
hex_digest = hash_object.hexdigest()
print(hex_digest)
# 374d794a95cdcfd8b35993185fef9ba368f160d8daf432d08ba9f1ed1e5abe6cc69291e0fa2fe0006a52570ef18c19def4e617c33ce52ef0a6e5fbe318cb0387
SHA-2ファミリーの比較
| アルゴリズム | 出力ビット | ブロックサイズ | 内部状態 | セキュリティレベル |
|---|---|---|---|---|
| SHA-224 | 224 | 512 | 256 | 112ビット |
| SHA-256 | 256 | 512 | 256 | 128ビット |
| SHA-384 | 384 | 1024 | 512 | 192ビット |
| SHA-512 | 512 | 1024 | 512 | 256ビット |
| SHA-512/224 | 224 | 1024 | 512 | 112ビット |
| SHA-512/256 | 256 | 1024 | 512 | 128ビット |
SHA-3(Keccak)
SHA-3はSHAファミリーの最新メンバーで、Keccakアルゴリズムに基づいています。SHA-2とは根本的に異なる構造(スポンジ構造)を使用し、アルゴリズムの多様性を提供します。
- 状態:安全、2015年に標準化
- 構造:スポンジ構造(SHA-2のMerkle-Damgardとは異なる)
- バリアント:SHA-3-224、SHA-3-256、SHA-3-384、SHA-3-512、SHAKE128、SHAKE256
- 用途:SHA-2が万が一危殆化した場合のバックアップ
MD5:まだ使われている理由
MD5は128ビットのハッシュを生成し、かつて最も人気のあるハッシュ関数でした。現在は暗号学的に破られていますが、セキュリティ以外の目的ではまだ使われています。
MD5を使ってはいけない用途:
- パスワードハッシュ
- デジタル署名
- 証明書の検証
- セキュリティに関わるあらゆるアプリケーション
MD5が許容される用途:
- 非暗号化チェックサム(偶発的な破損に対するファイル整合性の確認)
- コンテンツアドレス可能ストレージ(セキュリティが不要な場合)
- 重複排除
- レガシーシステムとの互換性
import hashlib
# MD5はファイルチェックサムでよく見かける
md5_hash = hashlib.md5(b"Hello, World!").hexdigest()
print(md5_hash) # 65a8e27d8879283831b664bd8b7f0ad4
BLAKE3:モダンな代替手段
BLAKE3は、SHA-2やSHA-3に比べて大幅なパフォーマンス向上を実現しつつ、強力なセキュリティ特性を維持する新しいハッシュ関数です。
BLAKE3の利点:
- 極めて高速:現代のCPUでSHA-256の4〜8倍高速
- 並列化可能:SIMD命令と複数コアを活用可能
- 安全:十分に分析されたBLAKE2とChaChaストリーム暗号に基づく
- 多用途:ハッシュ、MAC、KDF、XOFとして機能可能
use blake3;
fn main() {
let hash = blake3::hash(b"Hello, World!");
println!("{}", hash.to_hex());
// 288a26b2bfb0602c0c7c9e4bf714f53f46c090da7e7ab8a30af9bf6c8e3bf0f8
}
ハッシュ関数の用途
1. ファイル整合性とチェックサム
ハッシュ関数は、ファイルが転送中に破損や改ざんされていないことを検証します。
# チェックサムの生成
sha256sum ubuntu-24.04.iso > checksum.txt
# チェックサムの検証
sha256sum -c checksum.txt
# ubuntu-24.04.iso: OK
// JavaScriptでのファイル整合性検証
async function verifyFileIntegrity(file, expectedHash) {
const buffer = await file.arrayBuffer();
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const actualHash = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
return actualHash === expectedHash;
}
ハッシュジェネレーターを使えば、ファイルや文字列のチェックサムを簡単に計算できます。
2. パスワードハッシュ
パスワードを平文で保存することは重大なセキュリティ脆弱性です。ハッシュ関数を使えば、パスワードを保存せずに検証できます。
**重要:**パスワードハッシュに素のSHA-256を使わないでください。ソルティングを含み、意図的に低速な専用のパスワードハッシュ関数を使用してください。
推奨されるパスワードハッシュアルゴリズム:
| アルゴリズム | 状態 | 主な特徴 |
|---|---|---|
| Argon2id | 最良の選択 | メモリハード、GPU耐性 |
| bcrypt | 実績あり | 広くサポート、実戦テスト済み |
| scrypt | 良好 | メモリハード |
| PBKDF2 | 許容範囲 | NIST承認、広く利用可能 |
// Node.jsでbcryptを使ったパスワードハッシュ
import bcrypt from 'bcrypt';
// パスワードのハッシュ化
const saltRounds = 12;
const passwordHash = await bcrypt.hash('user_password', saltRounds);
// 結果: $2b$12$LJ3m6gEwO/fSFqCVXWLwOeR/dYtTVkRDCwoGLBE0Fg6voFEOB5viy
// パスワードの検証
const isValid = await bcrypt.compare('user_password', passwordHash);
// true
// Argon2id(新規アプリケーション推奨)
import argon2 from 'argon2';
const hash = await argon2.hash('user_password', {
type: argon2.argon2id,
memoryCost: 65536, // 64MB
timeCost: 3,
parallelism: 4,
});
const valid = await argon2.verify(hash, 'user_password');
素のハッシュだけでは不十分な理由:
// ダメな例: "password123"のSHA-256ハッシュ
ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f
// レインボーテーブルを持つ攻撃者はこれを即座に逆引きできます。
// 解決策: ソルト付きの適切なパスワードハッシュ関数を使用すること。
パスワードセキュリティについて詳しくはパスワードセキュリティガイドをご覧ください。強力なパスワードはパスワードジェネレーターで生成できます。
3. ブロックチェーンと暗号通貨
ハッシュ関数はブロックチェーン技術の基盤です。BitcoinはProof-of-Work合意メカニズムでSHA-256を使用しています。
import hashlib
import time
def mine_block(data, difficulty):
"""シンプルなProof-of-Workマイニングシミュレーション"""
prefix = '0' * difficulty
nonce = 0
while True:
text = f'{data}{nonce}'
hash_result = hashlib.sha256(text.encode()).hexdigest()
if hash_result.startswith(prefix):
return nonce, hash_result
nonce += 1
# 難易度4でブロックをマイニング(ハッシュが0000で始まる必要あり)
nonce, hash_value = mine_block("Block data here", 4)
print(f"Nonce: {nonce}")
print(f"Hash: {hash_value}")
# ハッシュは"0000..."で始まる
ブロックチェーンにおけるハッシュ関数:
- ブロックハッシュ:各ブロックは前のブロックのハッシュを含み、不変のチェーンを形成
- マークルツリー:トランザクションはツリー構造で整理され、各ノードは子のハッシュ
- マイニング:Proof-of-Workはターゲット値以下のハッシュを生成するnonceを見つける必要あり
- アドレス:暗号通貨のアドレスは公開鍵のハッシュから導出
4. デジタル署名
デジタル署名はハッシュ関数を使ってメッセージを効率的に署名します。メッセージ全体(非常に大きい可能性がある)に署名する代わりに、メッセージをハッシュし、そのハッシュに署名します。
// デジタル署名の簡略フロー
// 1. メッセージをハッシュ
const messageHash = await sha256(message);
// 2. 秘密鍵でハッシュに署名
const signature = await crypto.subtle.sign(
{ name: 'RSASSA-PKCS1-v1_5' },
privateKey,
encoder.encode(messageHash)
);
// 3. 検証:メッセージをハッシュし署名を確認
const verifyHash = await sha256(message);
const isValid = await crypto.subtle.verify(
{ name: 'RSASSA-PKCS1-v1_5' },
publicKey,
signature,
encoder.encode(verifyHash)
);
5. コンテンツアドレス可能ストレージ
Git、IPFS、Dockerなどのシステムは、ハッシュによってコンテンツをアドレス指定します。これにより、重複排除、整合性検証、不変性が実現されます。
# GitはオブジェクトIDにSHA-1を使用(SHA-256への移行中)
$ echo "Hello, World!" | git hash-object --stdin
8ab686eafeb1f44702738c8b0f24f2567c36da6d
# Dockerレイヤーのコンテンツアドレス指定
$ docker inspect --format='{{.Id}}' my-image
sha256:abc123...
6. HMAC(ハッシュベースメッセージ認証コード)
HMACはハッシュ関数と秘密鍵を組み合わせて、データの整合性と認証の両方を提供します。
// Node.jsでのHMAC
import { createHmac } from 'crypto';
function generateHmac(message, secretKey) {
return createHmac('sha256', secretKey)
.update(message)
.digest('hex');
}
// Webhook署名の検証
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = generateHmac(payload, secret);
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// APIリクエストの署名
const timestamp = Date.now().toString();
const body = JSON.stringify({ action: 'transfer', amount: 100 });
const signaturePayload = `${timestamp}.${body}`;
const signature = generateHmac(signaturePayload, API_SECRET);
fetch('https://api.example.com/transactions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Timestamp': timestamp,
'X-Signature': signature,
},
body: body,
});
7. サブリソース完全性(SRI)
ブラウザはSRIハッシュを使って、取得したリソース(スクリプト、スタイルシート)が改ざんされていないことを検証できます。
{/* SRI: ブラウザはスクリプト実行前にハッシュを検証 */}
<script
src="https://cdn.example.com/library.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"
></script>
<link
rel="stylesheet"
href="https://cdn.example.com/styles.css"
integrity="sha256-5IxE/yBJ7PB5TbXxYqBCj9n5xM4mECft3p7/+Gkh1I="
crossorigin="anonymous"
/>
# SRIハッシュの生成
echo -n "file contents" | openssl dgst -sha384 -binary | openssl base64 -A
# または: shasum -b -a 384 file.js | xxd -r -p | base64
8. データ重複排除
ハッシュ関数はストレージシステムで重複データブロックを識別します:
// コンテンツハッシュによるシンプルな重複排除
class DeduplicatedStorage {
constructor() {
this.store = new Map(); // hash -> data
this.references = new Map(); // hash -> reference count
}
async put(data) {
const hash = await sha256(data);
if (!this.store.has(hash)) {
this.store.set(hash, data);
this.references.set(hash, 1);
} else {
this.references.set(hash, this.references.get(hash) + 1);
}
return hash;
}
get(hash) {
return this.store.get(hash);
}
}
ハッシュ関数のセキュリティに関する考慮事項
既知の脆弱性
| アルゴリズム | 状態 | 既知の攻撃 |
|---|---|---|
| MD5 | 破られている | 衝突攻撃、実用的な偽造 |
| SHA-1 | 破られている | SHAttered衝突攻撃(2017年) |
| SHA-256 | 安全 | 実用的な攻撃は知られていない |
| SHA-512 | 安全 | 実用的な攻撃は知られていない |
| SHA-3 | 安全 | 実用的な攻撃は知られていない |
| BLAKE3 | 安全 | 実用的な攻撃は知られていない |
適切なハッシュ関数の選び方
汎用ハッシュ(チェックサム、データ整合性):
- セキュリティが重要な場面にはSHA-256
- パフォーマンスが重要な場面にはBLAKE3
- セキュリティに関係のないチェックサムにはMD5も許容
パスワードハッシュ:
- Argon2id(最良)
- bcrypt(実績あり)
- 素のSHA-256/SHA-512は使わないこと
デジタル署名:
- SHA-256またはSHA-384
- 長期セキュリティにはSHA-512
ブロックチェーン:
- SHA-256(Bitcoin、大半のチェーン)
- Keccak-256(Ethereum)
HMAC:
- HMAC-SHA-256(標準)
- HMAC-SHA-512(より高いセキュリティマージン)
長さ拡張攻撃
SHA-256とSHA-512(SHA-3やBLAKE3ではない)は長さ拡張攻撃に対して脆弱です。これは、hash(message)が与えられると、攻撃者が元のメッセージを知らなくてもhash(message || padding || extension)を計算できることを意味します。
**対策:**認証にはハッシュ関数の直接使用ではなくHMACを使用しましょう。HMACは長さ拡張攻撃に対して脆弱ではありません。
// 長さ拡張攻撃に脆弱
const token = sha256(secret + message);
// 安全: HMACを使用
const token = hmacSha256(secret, message);
ハッシュ関数の実装
JavaScript(ブラウザとNode.js)
// ブラウザ Web Crypto API
async function hash(algorithm, data) {
const encoder = new TextEncoder();
const encoded = encoder.encode(data);
const hashBuffer = await crypto.subtle.digest(algorithm, encoded);
return Array.from(new Uint8Array(hashBuffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
// 使用例
await hash('SHA-256', 'Hello, World!');
await hash('SHA-384', 'Hello, World!');
await hash('SHA-512', 'Hello, World!');
// Node.js cryptoモジュール
import { createHash } from 'crypto';
function hashNode(algorithm, data) {
return createHash(algorithm).update(data).digest('hex');
}
hashNode('sha256', 'Hello, World!');
hashNode('sha512', 'Hello, World!');
hashNode('md5', 'Hello, World!');
Python
import hashlib
# 主要なアルゴリズムすべて
data = b"Hello, World!"
print("MD5: ", hashlib.md5(data).hexdigest())
print("SHA-1: ", hashlib.sha1(data).hexdigest())
print("SHA-256:", hashlib.sha256(data).hexdigest())
print("SHA-384:", hashlib.sha384(data).hexdigest())
print("SHA-512:", hashlib.sha512(data).hexdigest())
print("SHA3-256:", hashlib.sha3_256(data).hexdigest())
# ファイルのハッシュ
def hash_file(filepath, algorithm='sha256'):
h = hashlib.new(algorithm)
with open(filepath, 'rb') as f:
while chunk := f.read(8192):
h.update(chunk)
return h.hexdigest()
Go
package main
import (
"crypto/sha256"
"crypto/sha512"
"fmt"
)
func main() {
data := []byte("Hello, World!")
// SHA-256
sha256Hash := sha256.Sum256(data)
fmt.Printf("SHA-256: %x\n", sha256Hash)
// SHA-512
sha512Hash := sha512.Sum512(data)
fmt.Printf("SHA-512: %x\n", sha512Hash)
}
パフォーマンス比較
ハッシュアルゴリズム間でパフォーマンスは大きく異なります。以下は現代のハードウェアでの概算ベンチマークです:
| アルゴリズム | 速度(MB/s) | 相対速度 |
|---|---|---|
| MD5 | 約700 | 1.4倍 |
| SHA-1 | 約550 | 1.1倍 |
| SHA-256 | 約500 | 1.0倍(基準) |
| SHA-512 | 約600 | 1.2倍(64ビット環境) |
| SHA-3-256 | 約400 | 0.8倍 |
| BLAKE3 | 約3000以上 | 6.0倍以上 |
注意:BLAKE3のパフォーマンスの優位性は、SIMD命令と並列処理を活用できることに由来します。実際のパフォーマンスはハードウェアと実装に依存します。
まとめ
暗号学的ハッシュ関数は、現代のコンピューティングとセキュリティの基本的な構成要素です。異なるハッシュアルゴリズムの特性、強み、適切な使用場面を理解することは、セキュリティ、データ整合性、分散システムに携わるすべての開発者にとって不可欠です。
重要なポイント:
- SHA-256が安全なデフォルト——ほとんどの暗号ハッシュのニーズに
- MD5やSHA-1をセキュリティ目的に使わない
- 専用のパスワードハッシュを使う(Argon2id、bcrypt)——素のハッシュ関数ではなく
- HMACは長さ拡張攻撃を防ぐ——認証にはこれを使用
- BLAKE3はパフォーマンスの王者——速度が必要なとき
- アバランシェ効果は味方——わずかな入力変更がまったく異なるハッシュを生成
- 衝突耐性が重要——実用的な衝突が知られていないアルゴリズムを選択
手軽にハッシュを生成・比較するなら、ハッシュジェネレーターをブックマークしてください。MD5、SHA-1、SHA-256、SHA-384、SHA-512に対応し、ブラウザで直接ハッシュの計算と検証ができます。
関連リソース
- ハッシュジェネレーター -- ハッシュを即座に生成
- パスワードセキュリティガイド -- 安全なパスワードの実践
- パスワードジェネレーター -- 強力なパスワードを生成
- APIセキュリティのベストプラクティス -- APIセキュリティにおけるハッシュ
- Base64エンコーディング解説 -- エンコーディングとハッシュの違い