2FA開発で知ったArgon2:パスワードハッシュ化の新時代

2FA開発で知ったArgon2:パスワードハッシュ化の新時代

作成日:
更新日:

「パスワードのハッシュ化?MD5でいいでしょ」

2FA(二要素認証)の開発を始めたとき、最初はそう思ってました。

MD5、SHA-1、SHA-256...どれも「ハッシュ関数」でしょ?違いなんてあるの?

でも、調べていくうちに、とんでもない勘違いをしていたことに気づきました。

パスワードのハッシュ化には、専用のアルゴリズムが必要なんです。

そして、その最適解がArgon2でした。

従来のハッシュ関数の問題点

MD5やSHA-1は「速すぎる」

MD5やSHA-1は、設計上、高速に動作するように作られています。

これは、データの整合性チェックやデジタル署名には最適です。でも、パスワードのハッシュ化には致命的な欠陥があります。

MD5: 1秒間に数億回のハッシュ計算が可能
SHA-1: 1秒間に数千万回のハッシュ計算が可能

これが何を意味するか?

ブルートフォース攻撃が簡単にできてしまうんです。

レインボーテーブル攻撃

レインボーテーブルとは、よく使われるパスワードとそのハッシュ値を事前に計算して保存したテーブルです。

MD5やSHA-1のような高速なハッシュ関数だと、数秒で数億パターンのハッシュを計算できます。

攻撃者がデータベースからハッシュ値を盗み出したら、レインボーテーブルと照合するだけで、多くのパスワードが即座に解読されてしまいます。

GPU攻撃の脅威

現代のGPUは、並列処理が得意です。

MD5やSHA-1のような単純なハッシュ関数は、GPUで並列実行すると、CPUの数百倍の速度で計算できます。

CPU: 1秒間に1000万回
GPU: 1秒間に100億回(1000倍!)

これでは、どんなに複雑なパスワードでも、時間をかければ解読されてしまいます。

Argon2とは

Argon2は、2015年のPassword Hashing Competitionで優勝した、パスワードハッシュ化専用のアルゴリズムです。

なぜArgon2が優勝したのか

Argon2が選ばれた理由は、3つの攻撃に対する耐性です:

  1. サイドチャネル攻撃に強い
  2. GPU攻撃に強い
  3. メモリ攻撃に強い

特に、メモリハードな設計が特徴的です。

メモリハードとは

メモリハードとは、計算に大量のメモリを必要とする設計のことです。

Argon2は、ハッシュ化の過程で大量のメモリを読み書きします。これにより:

  • GPU攻撃が困難: GPUはメモリ帯域幅が限られている
  • 専用ハードウェア攻撃が困難: メモリが高価になる
  • 並列化が困難: メモリ競合が発生する

結果として、ブルートフォース攻撃のコストが劇的に上がるんです。

Argon2の3つのバリアント

Argon2には、用途に応じて3つのバリアントがあります。

Argon2i(Independent)

サイドチャネル攻撃に最も強いバリアントです。

メモリアクセスのパターンがデータに依存しないため、タイミング攻撃やキャッシュ攻撃に耐性があります。

用途:

  • パスワードのハッシュ化
  • 暗号鍵の導出
  • 一般的な認証システム

Argon2d(Dependent)

GPU攻撃に最も強いバリアントです。

メモリアクセスのパターンがデータに依存するため、並列化が困難です。

用途:

  • 仮想通貨のマイニング
  • ブロックチェーン
  • GPU攻撃が懸念される環境

Argon2id(Hybrid)

Argon2iとArgon2dのハイブリッドです。

最初の部分はArgon2i、残りはArgon2dで処理します。

バランスが取れた選択肢で、多くの場合、これが推奨されます。

用途:

  • 一般的なアプリケーション(推奨)
  • セキュリティとパフォーマンスのバランスが重要

Argon2のパラメータ

Argon2は、3つのパラメータで調整できます。

1. 時間コスト(Time Cost)

反復回数を指定します。

timeCost = 3  → 3回反復
timeCost = 10 → 10回反復

値が大きいほど、計算時間が長くなり、セキュリティが向上します。

2. メモリコスト(Memory Cost)

使用するメモリ量を指定します(キロバイト単位)。

memoryCost = 65536  → 64MB
memoryCost = 131072 → 128MB

値が大きいほど、メモリ使用量が増え、GPU攻撃が困難になります。

3. 並列度(Parallelism)

並列スレッド数を指定します。

parallelism = 1  → シングルスレッド
parallelism = 4  → 4スレッド

一般的には、CPUのコア数に合わせます。

推奨設定

OWASP(Open Web Application Security Project)の推奨設定:

Argon2id
- timeCost: 2
- memoryCost: 65536 (64MB)
- parallelism: 1

この設定で、約0.5秒の計算時間になります。

実装例

Python(argon2-cffi)

from argon2 import PasswordHasher

# ハッシュ化
ph = PasswordHasher(
    time_cost=2,
    memory_cost=65536,
    parallelism=1,
    hash_len=32,
    salt_len=16
)

password = "my_secure_password"
hash = ph.hash(password)
# 結果: $argon2id$v=19$m=65536,t=2,p=1$...

# 検証
try:
    ph.verify(hash, password)
    print("パスワードが一致しました")
except:
    print("パスワードが一致しません")

Node.js(argon2)

const argon2 = require('argon2');

// ハッシュ化
async function hashPassword(password) {
  const hash = await argon2.hash(password, {
    type: argon2.argon2id,
    memoryCost: 65536, // 64MB
    timeCost: 2,
    parallelism: 1,
  });
  return hash;
}

// 検証
async function verifyPassword(hash, password) {
  try {
    await argon2.verify(hash, password);
    return true;
  } catch (err) {
    return false;
  }
}

PHP(password_hash)

PHP 7.2以降では、標準関数でArgon2が使えます。

// ハッシュ化
$options = [
    'memory_cost' => 65536, // 64MB
    'time_cost' => 2,
    'threads' => 1,
];

$hash = password_hash('my_secure_password', PASSWORD_ARGON2ID, $options);

// 検証
if (password_verify('my_secure_password', $hash)) {
    echo "パスワードが一致しました";
}

Argon2のメリット

1. セキュリティが高い

  • GPU攻撃に強い: メモリハードな設計
  • レインボーテーブル攻撃に強い: ソルトとメモリコスト
  • サイドチャネル攻撃に強い: Argon2i/Argon2id

2. 将来性がある

  • 標準化されている: RFC 9106として標準化
  • 広く採用されている: 多くのライブラリでサポート
  • 継続的に改善: コミュニティによる監査

3. 調整可能

  • 環境に応じて最適化: サーバーの性能に合わせて調整
  • セキュリティとパフォーマンスのバランス: 用途に応じて設定

Argon2のデメリット

1. 計算コストが高い

MD5やSHA-1と比べて、計算時間が長いです。

MD5: 0.001秒
SHA-256: 0.001秒
Argon2: 0.5秒(推奨設定)

でも、これはセキュリティのための投資です。

2. メモリ使用量が多い

推奨設定で64MBのメモリを使用します。

大量のユーザーが同時にログインすると、メモリ使用量が増えます。

でも、現代のサーバーなら、問題ないレベルです。

3. 古いハッシュとの互換性がない

既存のシステムでMD5やSHA-1を使っている場合、移行が必要です。

既存ユーザーのパスワードを、次回ログイン時にArgon2で再ハッシュ化する必要があります。

2FA開発での活用

2FA開発では、セキュリティトークンのハッシュ化にArgon2を使いました。

従来の実装(SHA-256)

import hashlib

def hash_token(token):
    return hashlib.sha256(token.encode()).hexdigest()

問題点:

  • 高速すぎる: ブルートフォース攻撃が容易
  • GPU攻撃に弱い: 並列化が簡単

Argon2での実装

from argon2 import PasswordHasher

ph = PasswordHasher(
    time_cost=2,
    memory_cost=65536,
    parallelism=1
)

def hash_token(token):
    return ph.hash(token)

def verify_token(hash, token):
    try:
        ph.verify(hash, token)
        return True
    except:
        return False

改善点:

  • セキュリティが向上: GPU攻撃に強い
  • 将来性がある: 標準化されたアルゴリズム
  • 調整可能: 環境に応じて最適化

まとめ

「MD5でいいでしょ」という考えは、完全に間違っていました

パスワードのハッシュ化には、専用のアルゴリズムが必要です。

Argon2は、その最適解です。

重要なポイント

  1. MD5やSHA-1は使わない: パスワードハッシュ化には不適切
  2. Argon2idを推奨: バランスが取れた選択肢
  3. パラメータを調整: 環境に応じて最適化
  4. 継続的に監視: セキュリティは継続的な改善が必要

今後の展望

2FA開発では、Argon2を標準として採用しました。

今後は:

  • 既存システムの移行: MD5/SHA-1からArgon2へ
  • パラメータの最適化: サーバー環境に応じて調整
  • セキュリティ監査: 定期的な見直し

セキュリティは、「これで大丈夫」という終わりがない分野です。

でも、Argon2を使うことで、正しい方向に進んでいることは確信できます。

参考資料