「パスワードをDBにそのまま保存してはいけない」ことは広く知られていますが、では何をどう保存すればよいのかまで、正確に説明できる人は意外と多くありません。
この記事では、パスワードの保存方法として「平文」「ハッシュ化のみ」「ソルト付きハッシュ」の3つを比較しながら、なぜソルト付きハッシュが推奨されるのかを整理します。
セキュリティ対策を横断的に確認したい方は、 Webアプリの脆弱性・セキュリティ対策まとめ もご覧ください。
実際に手を動かして確かめてみましょう。以下のデモは実際の暗号学的ハッシュ関数は使わず、簡易的な疑似計算で仕組みだけを可視化しています。
実際のハッシュ関数は使わず、簡易的な表現で仕組みを可視化します。DB流出時に何が漏れるかを確認してください。
「password123」という同じパスワードを使っている他のユーザーがいた場合を想像してみてください。
「平文保存」ではパスワードがそのまま読める状態で保存されます。「ハッシュのみ」に切り替えると読めない文字列に変わりますが、同じパスワードなら常に同じ値になるため、同じパスワードを使っている人がいれば見比べるだけで分かってしまいます。「ソルト付きハッシュ」では、同じパスワードでもユーザーごとに異なる値になり、この弱点が解消されます。
最も分かりやすい問題は、DBが万が一流出した場合、パスワードがそのまま第三者の手に渡ることです。
多くの利用者は複数のサービスで同じパスワードを使い回しているため、1つのサービスからの流出が、他のサービスへの不正ログイン(クレデンシャルスタッフィング)に直結します。平文保存は、自社サービスの被害だけでなく、利用者が使っている他サービスの被害まで引き起こしかねません。
ハッシュ関数は「同じ入力から常に同じ出力が得られる」「出力から入力を逆算するのが極めて困難」という性質を持ちます。この性質だけを見ると安全に思えますが、ハッシュ化のみの場合、次の攻撃に弱いという弱点があります。
つまり「逆算できない」ことと「攻撃に強い」ことは、必ずしもイコールではありません。
ソルトとは、ハッシュ化する前にパスワードへ付け足す、ユーザーごとにランダムな文字列のことです。
ハッシュ値 = hash(パスワード + ソルト)
同じパスワードでも、ソルトが異なれば結果のハッシュ値はまったく別のものになります。これにより、事前に用意したレインボーテーブルが通用しなくなり、同じパスワードを使っているユーザー同士がハッシュ値から特定されることもなくなります。ソルト自体は秘密にする必要がなく、多くの実装ではハッシュ値と一緒にDBへ保存します。
実務では、MD5やSHA-256のような高速なハッシュ関数をパスワード保存にそのまま使うことは推奨されません。理由は、計算が高速すぎるあまり、攻撃者が総当たりで大量のハッシュ値を試す速度も上がってしまうためです。
そこで使われるのが、bcryptやArgon2といった、意図的に計算コストを高くしたハッシュ関数です。これらはソルトの生成・付与を内部で自動的に行う設計になっており、開発者が個別にソルトを実装する必要がない点も実務上のメリットです。計算コストを表す「ラウンド数」や「メモリ使用量」はパラメータとして調整でき、サーバー性能とのバランスを見ながら設定します。
パスワードの保存は、平文はもちろん、ハッシュ化のみでも不十分です。ソルトを加えたうえで、bcryptやArgon2のような計算コストの高いハッシュ関数を使うことが、実務上の基本になります。
自社のアプリで、この設計になっているか一度確認してみてください。