SHA-256 vs bcrypt vs Argon2 - 開発者のためのハッシュアルゴリズム完全比較
パスワード保存にSHA-256を使ってはいけない理由、bcryptとArgon2の違い、2026年推奨ハッシュ戦略を詳しく解説します。
Toolypet Team
Development Team
SHA-256 vs bcrypt vs Argon2:いつ何を使うべきか?
「パスワードをSHA-256でハッシュして保存すれば安全でしょ?」
この質問をする開発者は多いです。結論から言えば、パスワードにSHA-256を使用してはいけません。
このガイドでは、ハッシュアルゴリズムの違いと各状況に適した正しい選択を解説します。
ハッシュ vs 暗号化:基本概念
ハッシュ(Hashing)
入力 → ハッシュ関数 → 固定長出力(復元不可)
"password" → SHA-256 → "5e884898da28047d..."
- 一方向:元のデータを復元できない
- 決定的:同じ入力 = 同じ出力
- 固定長:入力サイズに関係なく一定の出力
暗号化(Encryption)
入力 + 鍵 → 暗号化 → 暗号文 → 復号化 + 鍵 → 元データ
"password" + key → AES → "Xyz..." → AES + key → "password"
- 双方向:鍵で元データを復元可能
- 鍵依存:鍵がないと復号化不可
パスワード保存にはハッシュを使用します。 元データを知る必要はなく、入力値とハッシュを比較するだけです。
ハッシュアルゴリズムの分類
高速ハッシュ(Fast Hash)
| アルゴリズム | 出力長 | 速度 | 用途 |
|---|---|---|---|
| MD5 | 128ビット | 非常に高速 | ❌ セキュリティ用途禁止 |
| SHA-1 | 160ビット | 高速 | ❌ セキュリティ用途禁止 |
| SHA-256 | 256ビット | 高速 | ファイル整合性、デジタル署名 |
| SHA-512 | 512ビット | 高速 | ファイル整合性、ブロックチェーン |
低速ハッシュ(Slow Hash / Password Hash)
| アルゴリズム | 特徴 | 2026年推奨 |
|---|---|---|
| bcrypt | 時間調整可能(cost) | ✅ |
| scrypt | メモリ集約型 | ✅ |
| Argon2 | 最新、OWASP第1位 | ✅✅ |
| PBKDF2 | 互換性が高い | ⚠️ レガシー |
なぜSHA-256でパスワードを保存してはいけないのか?
理由1:速すぎる
SHA-256は速度のために設計されています。これはファイル整合性チェックには利点ですが、パスワード保存には致命的な欠点です。
現代GPUの性能:
- MD5:毎秒1,800億ハッシュ
- SHA-256:毎秒100億ハッシュ
- bcrypt(cost 12):毎秒1,000ハッシュ
8文字の複雑なパスワードでもSHA-256で保存すると数分でクラッキングされます。
理由2:Saltを手動で管理する必要がある
# ❌ 間違った方法
hash = sha256(password)
# 問題:レインボーテーブル攻撃に脆弱
# ⚠️ 改善したが依然として不十分
salt = generate_random_salt()
hash = sha256(salt + password)
# 問題:依然として速すぎる
理由3:GPUアクセラレーションに脆弱
SHA-256はGPUでの並列処理が容易です。攻撃者はゲーミングGPU数枚で膨大な速度でハッシュを計算できます。
bcrypt:27年間検証された標準
動作原理
bcrypt(cost, salt, password) → hash
cost:計算の反復回数(2^cost)
salt:22文字のランダム文字列(自動生成)
なぜbcryptが安全なのか?
- 意図的に遅い:cost factorで速度を調整
- 自動Salt:毎回異なるハッシュを生成
- メモリ集約型:GPUアクセラレーションが困難
Cost Factorガイド(2026年)
| Cost | ハッシュ時間 | 推奨用途 |
|---|---|---|
| 10 | 約100ms | 開発/テスト |
| 12 | 約250ms | 一般的なWebアプリ(推奨) |
| 13-14 | 約500ms | 高セキュリティ要件 |
| 15+ | 1秒以上 | 特殊目的 |
コード例
// Node.js with bcrypt
const bcrypt = require('bcrypt');
// ハッシュ(会員登録)
const hash = await bcrypt.hash(password, 12); // cost 12
// 検証(ログイン)
const isValid = await bcrypt.compare(inputPassword, storedHash);
# Python with bcrypt
import bcrypt
# ハッシュ
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
# 検証
is_valid = bcrypt.checkpw(input_password.encode(), stored_hash)
Argon2:次世代の標準
Argon2とは?
2015年Password Hashing Competitionの優勝者です。OWASPが2026年第1位推奨のアルゴリズムです。
Argon2の変形
| 変形 | 特徴 | 推奨 |
|---|---|---|
| Argon2d | GPU攻撃耐性 | サイドチャネル脆弱 |
| Argon2i | サイドチャネル耐性 | GPU攻撃脆弱 |
| Argon2id | d + iのハイブリッド | ✅ 推奨 |
Argon2のパラメータ
Argon2id(memory, iterations, parallelism, password, salt)
- memory:メモリ使用量(KB)
- iterations:反復回数
- parallelism:並列スレッド数
OWASP推奨設定(2026年)
最小設定:
- memory:64MB(65536 KB)
- iterations:3
- parallelism:4
高セキュリティ:
- memory:256MB
- iterations:4
- parallelism:8
コード例
// Node.js with argon2
const argon2 = require('argon2');
// ハッシュ
const hash = await argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 65536, // 64MB
timeCost: 3,
parallelism: 4
});
// 検証
const isValid = await argon2.verify(storedHash, inputPassword);
# Python with argon2-cffi
from argon2 import PasswordHasher
ph = PasswordHasher(
memory_cost=65536,
time_cost=3,
parallelism=4
)
# ハッシュ
hash = ph.hash(password)
# 検証
try:
ph.verify(stored_hash, input_password)
except VerifyMismatchError:
# パスワード不一致
bcrypt vs Argon2:何を選ぶべきか?
比較表
| 項目 | bcrypt | Argon2id |
|---|---|---|
| 年齢 | 1999年(27年) | 2015年(11年) |
| 検証 | 27年の実戦検証 | 学術検証完了 |
| メモリ調整 | ❌ | ✅ |
| GPU耐性 | ⚠️ 中程度 | ✅ 強い |
| ライブラリ | 全言語対応 | ほとんどの言語 |
| OWASP推奨 | 第2位 | 第1位 |
選択ガイド
bcryptを選択:
- レガシーシステムとの互換性が必要
- 検証された安定性を優先
- シンプルな設定を好む
Argon2idを選択:
- 新プロジェクトの開始
- 最新セキュリティ標準に準拠
- 高いGPU耐性が必要
2026年推奨順位(OWASP)
第1位:Argon2id
第2位:bcrypt
第3位:scrypt
第4位:PBKDF2(互換性が必要な場合)
SHA-256を使用すべき場合
SHA-256はパスワード以外の用途には完璧です。
適切な用途
| 用途 | 例 |
|---|---|
| ファイル整合性 | ダウンロード検証、バックアップ確認 |
| デジタル署名 | JWT、証明書 |
| ブロックチェーン | ビットコインマイニング |
| チェックサム | データ転送検証 |
| ハッシュテーブル | (HMACと共に)APIキー保存 |
コード例
// ファイルハッシュ(Node.js)
const crypto = require('crypto');
const fs = require('fs');
const fileBuffer = fs.readFileSync('file.zip');
const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
console.log(hash); // "a1b2c3d4..."
実践実装チェックリスト
パスワード保存
- Argon2idまたはbcryptを使用
- 適切なwork factorを設定(250-500ms)
- ライブラリの自動salt生成を使用
- ハッシュ結果全体を保存(salt含む)
パスワード検証
- タイミング攻撃防止の比較関数を使用
- 検証失敗時に一貫した応答時間
- ログイン失敗回数の制限
マイグレーション
- 新しいハッシュへの段階的アップグレード
- ログイン成功時に新アルゴリズムで再ハッシュ
- 古いハッシュを識別できるようにprefixを使用
FAQ
Q1:MD5で保存された既存のパスワードはどうすれば良いですか?
A:ユーザーがログインする際に新しいアルゴリズムで再ハッシュしてください。
// ログイン成功時
if (hash.startsWith('$md5$')) {
// bcryptで再ハッシュ
const newHash = await bcrypt.hash(inputPassword, 12);
await updateUserHash(userId, newHash);
}
Q2:bcryptの72バイト制限は問題になりませんか?
A:ほとんどのパスワードは72バイト以内です。より長い入力が必要な場合は、まずSHA-256でハッシュしてからbcryptを適用してください。
// 長いパスワードの処理
const prehash = crypto.createHash('sha256').update(password).digest('base64');
const finalHash = await bcrypt.hash(prehash, 12);
Q3:cost factorを高くしすぎるとDoS攻撃に脆弱になりませんか?
A:その通りです。ログインリクエストのレート制限(rate limiting)も一緒に実装してください。250-500msが適切なバランス点です。
Q4:pepperは必要ですか?
A:pepper(アプリケーションレベルの秘密鍵)は追加のセキュリティを提供しますが、必須ではありません。saltだけでも十分です。
Q5:オンラインハッシュツールを使っても良いですか?
A:学習/テスト目的でのみ使用してください。本番環境ではサーバーサイドでハッシュする必要があります。ハッシュジェネレーターは100%クライアントサイドで処理されるため安全です。
まとめ
| 用途 | 推奨アルゴリズム |
|---|---|
| パスワード保存 | Argon2id > bcrypt |
| ファイル整合性 | SHA-256 |
| デジタル署名 | SHA-256 / SHA-512 |
| レガシー互換 | bcrypt / PBKDF2 |
重要原則:
- パスワードに高速ハッシュ(SHA-256、MD5)は絶対禁止
- bcrypt cost 12以上、Argon2 memory 64MB以上
- ライブラリ提供のsalt自動生成を使用
関連ツール
| ツール | 用途 |
|---|---|
| Hash Generator | SHA-256、bcryptハッシュ生成 |
| Password Generator | 強力なパスワード生成 |
著者について
Toolypet Team
Development Team
The Toolypet Team creates free, privacy-focused web tools for developers and designers. All tools run entirely in your browser with no data sent to servers.