【入門編】パスワードハッシュ化におけるソルトとストレッチングの適切な実装 – アプリケーションセキュリティ & 安全な開発防御ガイド

こんにちは。セキュリティの世界へようこそ。

「パスワードを保存するときはハッシュ化しなさい」と教わったことはあるでしょう。でも、なぜ「ただのハッシュ化」では不十分なのか、なぜ「ソルト」や「ストレッチング」という少し面倒な手順が必要なのか、その裏側にある「泥棒との追いかけっこ」の仕組みを、今日は現場の視点から紐解いていきたいと思います。

—

1. なぜ「そのまま」保存してはいけないのか?

想像してみてください。あなたは自分の家の鍵を、玄関のドアの横にデカデカと「これは私の家の鍵です」と貼り付けておきますか? もちろん、しませんよね。

Webサービスにおいて、パスワードをそのまま(平文で)保存するのは、まさにこれと同じです。もしデータベースが盗まれたら、ユーザー全員の鍵が丸裸になってしまいます。

そこで登場するのが「ハッシュ化」です。これはパスワードを一度ぐちゃぐちゃに混ぜて、元の文字が分からない文字列(ハッシュ値)に変換する魔法です。しかし、現代の攻撃者は非常に賢い。彼らは「よく使われるパスワードリスト」を持っていて、片っ端からハッシュ化して、盗んだものと照らし合わせる「レインボーテーブル攻撃」という手法を使います。

2. 泥棒を足止めする「ソルト」と「ストレッチング」

ここで、我々セキュリティエンジニアが使う二つの「防犯アイテム」を紹介します。

ソルト(Salt):同じ鍵を一つずつ別物にする

「ソルト」は、パスワードの末尾にランダムな文字列(塩)を振りかけることです。
たとえば、「password123」というパスワードに、ユーザーごとに異なるランダムな文字列を足してからハッシュ化します。こうすると、たとえ同じパスワードを使っているユーザーがいても、データベースに保存されるハッシュ値は全く別のものになります。

攻撃者は「全員分まとめて解析する」ことができなくなり、一人ずつ地道に解読しなければなりません。これだけで、攻撃の効率は劇的に下がります。

ストレッチング(Stretching):泥棒をマラソンさせる

「ストレッチング」は、計算にわざと時間をかけさせる手法です。
最新のPCは強力で、1秒間に何十億回もパスワードを推測できます。そこで、あえてハッシュ計算を何万回も繰り返す(あるいは計算負荷を高くする)ことで、1つのパスワードを試すのにわざと0.5秒〜1秒かかるように調整します。

攻撃者が100万回試行するのに、数秒ではなく数日かかるようにする。「鍵を壊すのに、金庫を叩き壊すより時間がかかる」状況を作り出すのです。

—

3. 今、選ぶべきは「Argon2id」

昔は「bcrypt」という手法が主流でしたが、今はより強固な「Argon2id」が世界標準です。これは、メモリを大量に消費させる仕組みがあり、攻撃者が専用の特殊なマシンを使っても計算を効率化できないよう工夫されています。

実装のヒント:適切なWork Factorの設定

「計算コスト」を設定する際、難しく考えすぎる必要はありません。目安は「サーバーがレスポンスを返すのに耐えられるギリギリの重さ」です。

// Argon2を使ったパスワードハッシュ化のイメージ(Node.js環境)
const argon2 = require(‘argon2’);

async function hashPassword(password) {
try {
// 適切なパラメーター設定
const hash = await argon2.hash(password, {
type: argon2.argon2id, // 推奨アルゴリズム
memoryCost: 2 16, // 64MB程度のメモリを消費させる
timeCost: 3, // 3回繰り返す(計算コスト)
parallelism: 1 // CPUコアの並列利用数
});
return hash;
} catch (err) {
console.error(“ハッシュ化失敗:”, err);
}
}

現場の運用ポイント:

  • メモリコスト(memoryCost): サーバーのスペックに合わせて設定しますが、低すぎると攻撃者にとって解析が楽になります。64MB程度からテストし、ログイン処理が重すぎないか確認しましょう。
  • CPUコスト(timeCost): サーバーの負荷を見て調整します。ユーザーが「ログインに時間がかかるな」とストレスを感じない程度、かつセキュリティを担保できる絶妙なラインを探るのが、プロの仕事です。

—

最後に:完璧な防御はない、だからこそ「遅延」させる

セキュリティにおいて「絶対に破られない」ものは存在しません。しかし、攻撃者にとって「割に合わない」と思わせることは可能です。

パスワード保護の本質は、「どれだけ攻撃者の時間を奪えるか」という一点に尽きます。泥棒が金庫を開けるのを諦めて別の家を探すように、あなたのサービスを「狙うだけ無駄だ」と思わせることができれば、その時点であなたの勝利です。

これから開発現場でパスワードを扱うときは、ぜひ今日学んだ「ソルト」と「ストレッチング」を思い出してください。一歩ずつ、強固なサービスを作っていきましょうね!

コメント

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