はじめに
サイバーセキュリティの世界では、ユーザー認証情報の保護が最重要です。パスワードは、保存される際にプレーンテキストで保持されることはほとんどありません。代わりに、通常はハッシュ化されます。これは、一方向の暗号化関数を使用して、固定サイズの文字列表現に変換されることを意味します。これにより、攻撃者がデータベースへのアクセス権を取得した場合でも、パスワードに直接アクセスされることを防ぎます。しかし、単純なハッシュだけでは不十分です。攻撃者は、事前に計算されたハッシュのテーブル(レインボーテーブル)やブルートフォース攻撃を使用してパスワードを解読できます。
ここで「ソルト(salt)」が登場します。ソルトとは、パスワードをハッシュ化する前に、ユニークでランダムな文字列(「ソルト」)を追加する技術です。このソルトは、ハッシュと共に保存されます。ユーザーがログインを試みる際、同じソルトが取得され、入力されたパスワードと組み合わされてからハッシュ化され、生成されたハッシュが保存されているハッシュと比較されます。
この実験(Lab)では、パスワードのソルト化の概念を探求し、セキュリティ強化におけるその重要性を理解し、John the Ripper のような強力なパスワード解読ツールがソルト化されたハッシュとどのように相互作用するかを観察します。ソルト化されたハッシュを識別する方法、John the Ripper がそれらをどのように処理するかを確認し、パスワード解読の難易度にソルト化が与える大きな影響を把握します。最後に、実際のパスワードストレージでソルト化がどのように実装されているかの一端を垣間見ることができます。
ソルト化の概念を理解する
このステップでは、パスワードのソルト化の基本的な概念と、それがパスワードセキュリティの強化に不可欠である理由を学びます。
パスワードが「ソルト化」されるとき、ハッシュ化される前にユニークでランダムな文字列(ソルト)がパスワードに追加されます。これは、たとえ 2 人のユーザーが全く同じパスワードを持っていても、それぞれに異なるソルトが使用されたため、保存されるハッシュは異なることを意味します。これにより、特定のパスワードに対する固定ハッシュに依存するレインボーテーブルを効果的に無効化できます。また、攻撃者は単にすべての可能なパスワードだけでなく、すべての可能なパスワードとすべての可能なソルトのハッシュを計算する必要があるため、ブルートフォース攻撃もより困難になります。
ソルト化されたハッシュの例を見てみましょう。~/project ディレクトリに作成された salted_passwords.txt ファイルを開きます。
cat ~/project/salted_passwords.txt
以下のような出力が表示されるはずです。
user1:$6$salt12345$abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ./:user1password
user2:5f4dcc3b5aa765d61d8327deb882cf99
user3:$6$another_salt$zyxwuvtsrqponmlkjihgfedcba9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA./:anotherpassword
user4:$6$short$abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ./:shortpass
user1、user3、user4 の行に注目してください。これらには、$6$salt12345$ や $6$another_salt$ のようなセクションが含まれています。$6$ は使用されたハッシュ化アルゴリズム(この場合は SHA-512)を示し、2 番目と 3 番目の $ の間の文字列(salt12345 または another_salt)がソルトです。3 番目の $ より後の残りの文字列は、ソルトと組み合わされたパスワードの実際のハッシュです。user2 の行は、後で比較に使用するソルト化されていない MD5 ハッシュです。
重要な点は、各ソルト化されたハッシュにはユニークなソルトがあり、同じ事前計算済みテーブルを使用して複数のパスワードを同時に解読することをより困難にしているということです。
ソルト化されたハッシュを識別する
このステップでは、一般的な形式に基づいてソルト化されたハッシュを識別する方法を学びます。これらの形式を認識することは、パスワードの解読や防御へのアプローチの最初のステップです。
異なるハッシュ化アルゴリズムやシステムでは、ソルト化されたハッシュを保存するためにさまざまな形式が使用されます。しかし、多くの一般的な形式、特に Linux システムで使用される形式は、ソルトがハッシュ文字列内に直接埋め込まれ、しばしば $ のような特殊文字で区切られるパターンに従います。
salted_passwords.txt ファイルを再度確認し、ソルト化を示唆するパターンを特定しましょう。
cat ~/project/salted_passwords.txt
前のステップで観察したように:
user1:$6$salt12345$abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ./:user1passworduser3:$6$another_salt$zyxwuvtsrqponmlkjihgfedcba9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA./:anotherpassworduser4:$6$short$abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ./:shortpass
これらの行は、$6$ というプレフィックス(SHA-512 crypt を示す)に続いてソルト文字列(例:salt12345、another_salt、short)、そして実際のハッシュが明確に示されています。この構造は、ソルト化されたハッシュの強力な指標となります。
対照的に、user2 の行は次のようになります。
user2:5f4dcc3b5aa765d61d8327deb882cf99
これは単純な MD5 ハッシュです。区切り文字や埋め込まれたソルトがなく、ソルト化されていないハッシュとして容易に識別できます。
ソルト化されたハッシュとソルト化されていないハッシュを迅速に区別できる能力は、セキュリティ専門家やペネトレーションテスターにとって極めて重要です。なぜなら、それは採用できる解読戦略を決定するからです。
John the Ripper によるソルト化ハッシュの処理を観察する
このステップでは、人気のパスワードクラッキングツールである John the Ripper を使用して、ソルト化されたハッシュとソルト化されていないハッシュの両方をどのように処理するかを観察します。これにより、ソルト化の実用的な影響が実証されます。
まず、簡単な単語リストを使用して salted_passwords.txt のパスワードをクラックしてみましょう。このデモンストレーションでは、小さくカスタムされた単語リストを使用します。
~/project ディレクトリに wordlist.txt という名前の単語リストファイルを作成し、いくつかの一般的なパスワードを追加します。
echo "password" > ~/project/wordlist.txt
echo "user1password" >> ~/project/wordlist.txt
echo "anotherpassword" >> ~/project/wordlist.txt
echo "shortpass" >> ~/project/wordlist.txt
echo "123456" >> ~/project/wordlist.txt
次に、この単語リストを使用して、salted_passwords.txt ファイルに対して John the Ripper を実行します。
john --wordlist=~/project/wordlist.txt ~/project/salted_passwords.txt
John the Ripper がハッシュを分析します。パスワードのクラッキングを試みていることを示す出力が表示されるはずです。ソルト化されていない MD5 ハッシュであり、「password」が単語リストに含まれているため、user2 のパスワードはすぐに発見される可能性が高いです。対応する平文が単語リストに含まれている場合、ソルト化されたパスワードも発見されるでしょう。
出力例は次のようになります。
Using default input encoding: UTF-8
Loaded 4 password hashes with no different salts to crack (crypt, generic crypt(3) [MD5/Blowfish/SHA1/SHA256/SHA512/XSHAa/XSHAab/bcrypt/scrypt])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
password (user2)
user1password (user1)
anotherpassword (user3)
shortpass (user4)
4g 0:00:00:00 DONE (2023-10-27 10:30) ...
Session completed.
John が終了した後、--show オプションを使用してクラックされたパスワードを表示できます。
john --show ~/project/salted_passwords.txt
このコマンドは、John が正常にクラックしたパスワードを表示します。
user2:password (user2)
user1:user1password (user1)
user3:anotherpassword (user3)
user4:shortpass (user4)
4 password hashes cracked, 0 left
John the Ripper は、埋め込まれたソルトを含むさまざまなハッシュ形式を処理するように設計されています。ソルトを自動的に抽出し、クラッキングプロセス中に使用します。しかし、パスワードごとにユニークなソルトが存在することは、特に大規模な攻撃において、クラッキングに必要な計算量を大幅に増加させます。
ソルト化がクラッキングに与える影響を理解する
このステップでは、ソルト化がパスワードクラッキングを、特に大規模な攻撃において、著しく困難にする理由についての理解を深めます。
salted_passwords.txt ファイルの user2 のエントリを見てみましょう:user2:5f4dcc3b5aa765d61d8327deb882cf99。これはパスワード "password" のソルト化されていない MD5 ハッシュです。攻撃者がソルト化されていない MD5 ハッシュのデータベースを入手した場合、以下のことが可能になります。
- レインボーテーブルの使用: 一般的なパスワードのハッシュを事前に計算したテーブルです。「password」がテーブルに含まれている場合、そのハッシュ
5f4dcc3b5aa765d61d8327deb882cf99もそこにあり、平文を即座に取得できます。 - 複数のパスワードの同時クラッキング: 多くのユーザーが同じ弱いパスワード(例:"123456")を使用している場合、それらのソルト化されていないハッシュは同一になります。攻撃者は、そのハッシュのインスタンスを 1 つクラックするだけで、それを共有しているすべてのユーザーのパスワードを知ることができます。
次に、ソルト化されたハッシュを見てみましょう。
user1:$6$salt12345$abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ./:user1passworduser3:$6$another_salt$zyxwuvtsrqponmlkjihgfedcba9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA./:anotherpassword
たとえ user1 と user3 が全く同じパスワードを持っていたとしても、ソルト(salt12345 と another_salt)が異なるため、ハッシュは異なります。これには、攻撃者にとっていくつかの重要な影響があります。
- レインボーテーブルは無効: 各ハッシュはソルトにより一意であるため、事前に計算されたレインボーテーブルは効果がなくなります。攻撃者は、考えられるすべてのソルトに対してレインボーテーブルが必要になりますが、これは計算量的に不可能です。
- ハッシュごとのクラッキング: 攻撃者は、各ソルト化されたハッシュを個別にクラックする必要があります。100 人のユーザーが同じパスワードを持っていても、それぞれにユニークなソルトがある場合、攻撃者はユニークなハッシュとソルトのペアごとに、100 回の別々のクラッキング操作を実行する必要があります。これにより、攻撃が成功するために必要な時間とリソースが劇的に増加します。
- 総当たり攻撃の困難性: 総当たり攻撃の場合、攻撃者はハッシュ化して比較する前に、各候補パスワードをその特定のソルトと組み合わせる必要があります。これは複雑さを増し、ソルト化されていないハッシュと比較してクラッキングプロセスを大幅に遅くします。
本質的に、ソルト化は単一のクラッキング問題を多くの独立したクラッキング問題に変換し、大規模なパスワード侵害を攻撃者にとってより困難で時間のかかるものにします。
パスワードストレージにおけるソルト化の実装
このステップでは、実際のパスワードストレージシステムでソルト化がどのように実装されているかについての概念的な理解を得ます。完全なシステムを構築するわけではありませんが、主要なコンポーネントを確認します。
ユーザーがパスワードを設定または変更する際、セキュアなシステムは通常、以下の手順を実行します。
- ランダムなソルトの生成: 暗号学的に安全なランダムなソルトが生成されます。このソルトは、ユーザーごと、およびパスワード変更ごとに一意であるべきです。
- パスワードとソルトの結合: ユーザーの平文パスワードが、生成されたソルトと連結されます。
- 結合された文字列のハッシュ化: 結合されたパスワードとソルトの文字列は、強力で遅いハッシュアルゴリズム(例:bcrypt、scrypt、Argon2、または例で見られる SHA-512 crypt)を通過させられます。遅いハッシュアルゴリズムは、総当たり攻撃をさらに時間のかかるものにするため、好まれます。
- ハッシュとソルトの保存: 結果のハッシュとソルトは、データベースに一緒に保存されます。ソルトは秘密である必要はありません。その目的は、ハッシュの一意性を保証することです。
ユーザーがログインを試みる際:
- 保存されたソルトの取得: システムは、データベースからユーザーのアカウントに関連付けられたソルトを取得します。
- 入力されたパスワードと保存されたソルトの結合: ユーザーが入力したパスワードが、取得されたソルトと結合されます。
- 結合された文字列のハッシュ化: この結合された文字列は、登録時に使用されたものと同じハッシュアルゴリズムを使用してハッシュ化されます。
- ハッシュの比較: 新しく計算されたハッシュが、保存されているハッシュと比較されます。一致した場合、パスワードは正しいと判断されます。
mkpasswd コマンドを使用してソルト化されたハッシュを生成するシミュレーションを行いましょう。これは whois パッケージの一部であり、crypt(3) スタイルのハッシュを生成できます。
まず、whois がインストールされていることを確認します。
sudo apt install -y whois
次に、カスタムソルト "mysalt" を使用して、パスワード "mysecretpassword" の SHA-512 ソルト化ハッシュを生成します。
mkpasswd -m sha-512 "mysecretpassword" -s "mysalt"
以下のような出力が表示されます。
$6$mysalt$some_long_hash_string_here
出力 $6$mysalt$some_long_hash_string_here はソルト化されたハッシュです。$6$ は SHA-512 を示し、mysalt は提供したソルト、残りは実際のハッシュです。実際のシステムでは、ソルトは手動で提供されるのではなく、ランダムに生成されます。
これは基本的なプロセスを示しています。現代のアプリケーションは、これらの詳細を抽象化するライブラリやフレームワークを使用していますが、ユニークなソルトを生成し、それをパスワードと結合し、ハッシュ化し、両方を保存するという根本的な原則は、安全なパスワードストレージの基盤であり続けています。
まとめ
この実験では、パスワードのソルト化とそのパスワードセキュリティ強化における重要な役割について包括的な理解を得ました。ソルト化とは、ハッシュ化する前にパスワードにユニークでランダムな文字列(ソルト)を追加することで、同じパスワードであっても各ハッシュをユニークにするプロセスであることを学びました。
$ のような区切り文字と埋め込まれたソルト文字列を含む特徴的なフォーマットによって、ソルト化されたハッシュを特定することができました。John the Ripper を用いた実践的な観察を通じて、この強力なツールがソルト化されたハッシュとソルト化されていないハッシュの両方をどのように処理するか、そして特に、ソルト化がクラッキングプロセスをハッシュごとに実行させることを強制し、攻撃者の計算上の労力を大幅に増加させるかを理解しました。
最後に、パスワードストレージシステムにおけるソルト化の概念的な実装を探求し、ソルトとパスワードの生成、結合、ハッシュ化、保存に関わるステップを理解しました。この知識は、システム防御であれ、攻撃手法の理解であれ、サイバーセキュリティに関わるすべての人にとって不可欠です。
各パスワードハッシュをユニークにすることで、ソルト化はレインボーテーブル攻撃の脅威を効果的に軽減し、総当たり攻撃を劇的に遅延させるため、堅牢なパスワードセキュリティのための不可欠な技術となっています。


