【入門編】 ハッシュ関数の脆弱性と衝突攻撃の歴史的背景 – 暗号理論・認証基盤 & エンドポイントセキュリティ防御ガイド

こんにちは!ITインフラや開発の現場に飛び込んだばかりの頃は、専門用語の嵐に圧倒されてしまいますよね。「ハッシュ?衝突攻撃?なんだか難しそう……」と感じている方もご安心ください。

今回は、私たちが日々使っているパスワードやデジタルデータの安全性を支える「ハッシュ関数」の歴史と、そこに潜む恐ろしい「衝突攻撃」の裏側について、身近な防犯のたとえ話を交えながら、一歩ずつ優しく紐解いていきたいと思います。

セキュリティの泥臭い現実を知ることは、頑丈なシステムを作るための第一歩です。一緒に楽しく学んでいきましょう!

—

1. 家の鍵で例える「ハッシュ関数」と「衝突」の正体

まずは、ハッシュ関数がどんなものか、私たちの身近な「合鍵」にたとえて考えてみましょう。

ハッシュ関数ってなに?

ハッシュ関数とは、どんなに長いデータ(たとえば、A4用紙100枚分の文章や、あなたのパスワード)を入力しても、「決まった長さの短い文字列(暗号のようなもの)」に変換してくれる仕組みのことです。この変換された文字列のことを「ハッシュ値」と呼びます。

例えば、あなたがパスワードとして password123 と入力したとします。これをハッシュ関数に通すと、システム側には ef92b7... のような全く別の文字列として保存されます。
ここでの重要なポイントは、「ハッシュ値から元のパスワード(password123)を逆算することは、理論上できない(不可逆性)」という点です。システムはパスワードの「現物」を持たず、「ハッシュ値」だけを金庫にしまっているわけですね。

「衝突(コリジョン)」とはどういう状態?

さて、ここで事件が起きます。
世の中のすべてのデータ(無限の組み合わせ)を、決まった長さの短いハッシュ値に変換するわけですから、いつかは「違うデータなのに、たまたま同じハッシュ値になってしまうペア」が生まれてしまいます。

これをセキュリティの世界では「ハッシュ衝突(コリジョン)」と呼びます。

防犯にたとえるなら、「全く違う形をした2本の別の家の鍵なのに、なぜか同じ玄関の鍵穴に入ってしまう奇跡の欠陥」を見つけてしまったようなものです。泥棒がこの「別の鍵(同じハッシュになる偽データ)」を作ることができれば、本物の鍵を持っている主人になりすまして、簡単に家に入れてしまいますよね。

—

2. 歴史が証明した「MD5」と「SHA-1」の崩壊

かつて、このハッシュ関数として広く使われていたのが MD5 や SHA-1 という古いアルゴリズムです。しかし、コンピュータの性能が劇的に向上し、天才的な数学者たちが攻撃手法を発見したことで、これらは次々と「破られ」ました。

MD5の敗北(2004年〜)

MD5はかつて、ファイルのダウンロードが正しく行われたかを確認する(チェックサム)目的などで世界中で使われていました。しかし、2004年に中国の研究チームが「全く中身が違うのに、計算結果(ハッシュ値)が完全に一致する2つのファイル」を作り出すことに成功しました。
これにより、「信頼できる本物のソフトウェアのふりをして、中身がマルウェア(ウイルス)である偽ファイルを配る」ことが現実のものとなりました。

SHA-1の退場(2017年)

MD5の反省を生かして作られたのが SHA-1 です。こちらはより強力でしたが、2017年に大手IT企業の研究チームが、ついにSHA-1でも衝突攻撃に成功(通称「SHAttered」攻撃)しました。
これにより、Webサイトの通信を保護するSSL/TLS証明書や、Gitなどのソースコード管理システムでもSHA-1の利用は完全に禁止され、現在はより安全な SHA-2 や SHA-3 への移行が義務付けられています。

—

3. 現代の認証基盤への影響と、私たちが取るべき対策

「古いハッシュが危ないのはわかったけど、今のシステムはどうなっているの?」と思いますよね。
現代のWebサービスやインフラストラクチャでは、SHA-2(SHA-256など)やSHA-3が標準的に使われており、安全性が保たれています。

しかし、パスワードの保存においては、SHA-2のような「速すぎるハッシュ関数」をそのまま使うことも実は危険です。なぜなら、今の高性能なGPU(グラフィックボード)を使えば、1秒間に何十億通りものパスワードを総当たりで試すことができてしまうからです。

ここで登場するのが、「ソルト(Salt)」と「ストレッチング(Stretching)」という実践的な防御テクニックです。

実装例:PHPで安全にパスワードを保存する

現代のWeb開発において、私たちが一からハッシュ関数を実装する必要はありません。主要なプログラミング言語には、安全なアルゴリズムが標準で用意されています。

例えば、PHPでは password_hash() 関数を使うだけで、内部で自動的にソルトの生成とストレッチング(処理をあえて遅くして総当たり攻撃を防ぐ仕組み)を行ってくれます。

<?php
// ユーザーが入力したパスワード(実際にはフォーム等から受け取ります)
$plain_password = "MySecurePassword123!";

// password_hash関数を使い、安全なアルゴリズム(BCRYPTまたはArgon2)でハッシュ化する
// この関数を使うだけで、自動的にランダムな「ソルト」が付与され、ストレッチングも実行されます
$hashed_password = password_hash($plain_password, PASSWORD_DEFAULT);

// データベースには、この $hashed_password を保存します
// 元のパスワードは絶対に保存してはいけません!
echo "データベースに保存する値: " . $hashed_password . "\n";

// --- ログイン時の検証プロセス ---
// ユーザーが入力したパスワードが正しいかチェックする場合
$input_password = "MySecurePassword123!";

if (password_verify($input_password, $hashed_password)) {
    echo "認証成功:パスワードが一致しました!\n";
} else {
    echo "認証失敗:パスワードが間違っています。\n";
}
?>

このコードのポイントは以下の通りです:

  • ソルト(Salt): パスワードにランダムな文字列を「ひと味(塩)」として付け加えてからハッシュ化します。これにより、同じパスワードを使っているユーザーがいても、データベース上のハッシュ値は全く違うものになり、辞書攻撃(あらかじめ計算されたハッシュリストを使った攻撃)が無効化されます。
  • ストレッチング(Stretching): あえてハッシュ化の計算を何万回も繰り返し実行し、処理にわずかな時間(コンマ数秒)をかけさせます。人間がログインする時には気にならない遅延ですが、攻撃者が1秒間に何億回もパスワードを試すことを物理的に不可能にします。

—

まとめ

今回は、ハッシュ関数の基礎から、MD5やSHA-1の歴史的な衝突攻撃、そして現代のパスワード保護における実用的な対策まで見てきました。

セキュリティの世界は、攻撃者と防御者の「いたちごっこ」の歴史です。「昔は安全だった技術も、時代の進化とともに破られる」という事実を知っておくことが、エンジニアとしての確実な盾になります。

難しく考えず、まずは「古いアルゴリズムは使わない」「パスワード保存には専用の安全な関数(password_hashなど)を使う」という基本ルールをしっかり押さえていきましょう。一歩ずつ、確実にセキュアな開発者への道を歩んでいきましょうね!

コメント

タイトルとURLをコピーしました