セキュリティ

情報漏洩対策にソルト&ペッパーという技術:ハッシュとレインボーテーブル、リスト型攻撃

昨今、生成AIの発展とともに、情報の不正な取得を試みる攻撃パターンが急速に多様化しています。このため、企業は残念ながら「いつか情報漏洩する」という前提で必要な対策を行う必要があります。

一般的なウェブシステムにおける暗号化の限界点

そのため普遍的に用いられる技術の一つが「暗号化」です。しかし、高価な専用ハードウェアやチップを用いない一般的なウェブシステムの暗号化技術には比較的早く限界が訪れます。例えばデータベースを暗号化しても、フロントエンドなど、システムのどこかに必ず暗号を復号できるノードが存在します。フロントエンドに侵入されれば、データベースの暗号化だけでは情報漏洩を防ぐことは困難です。もちろんそれらを防ぐ暗号化技術も存在していますが、それらはコスト・難易度共に高くなってしまい、一般的なシステムへの実装は困難です。(パスポートやクレジットカードのチップなどはコストより安全性を優先し、それらの技術が利用されています。)

ハッシュ化による対策

とはいえ暗号化はコモンセンスを得ている最低限の対策として必須です。これは開発チームの意識の問題でもあります。クラウドによりストレージなどが数クリックで暗号ができるようになった今、暗号化を行わないという選択をとっている開発チームは、万が一の緊急時にステークホルダーの目に触れた際、「セキュリティ意識が低かった」と判断される危険性があります。

話を本題に戻すと、漏洩の可能性を踏まえた対策が「ハッシュ化」です。ハッシュ関数を使って元の値をわからなくします。例えばよく使われるsha-2というアルゴリズムを使い私の名前をハッシュ化させると以下となります。

kameda harunobub37bf6097f8139ea6feab1380b54deda95d6ede00db4e2c5a5590ec90d2975a7

対象が名前であればウェブサービスはユーザーに表示させるため、元の値の保存は必要になりますが、これがパスワードやPIN番号などは、昨今のウェブサービスのスタンダードとして、サーバ側が保存しているその値をユーザーに表示させる必要はないためハッシュ化された値のみを保存しておき、元の値を保存しないことで、データベースから情報が漏洩した際、機密情報の漏洩を防ぐことができます。

ユーザーが、例えばサイトへのログインの際にパスワードを入力したときは、同じようにハッシュ化した値と保存されている値を参照することで、元のパスワードを保存していなくても、パスワードが合致するかどうかを判断することができます。

なおこの際、古いハッシュ関数を使ったままですと、攻撃者は意図的に同じハッシュ値を生成できる異なる元の文字列を作り出すことができるため、常に新しい関数を使うことが重要です。

レインボーテーブル

原則ハッシュ関数は不可逆であり、

kameda harunobub37bf6097f8139ea6feab1380b54deda95d6ede00db4e2c5a5590ec90d2975a7

は計算可能ですが、逆の

b37bf6097f8139ea6feab1380b54deda95d6ede00db4e2c5a5590ec90d2975a7kameda harunobu

は計算が非常に困難です。しかしながら昨今、ハッシュ化が行われた情報が漏洩してしまった場合に、元の機密情報が特定されるケースがあります。それがレインボーテーブルといわれるものによる攻撃です。考え方はとにかく単純です。

https://ja.wikipedia.org/wiki/%E3%83%AC%E3%82%A4%E3%83%B3%E3%83%9C%E3%83%BC%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB

よく使われるパスワードとあらかじめハッシュ化した値のペアを攻撃者は持っています。システムからハッシュ化した値が漏洩した場合、そのテーブルと突き合わせることで元の値を手に入れることができます。パスワードがある程度の文字列長な場合、この攻撃は要求されるテーブルが膨大になるためあまり効果をあげませんが、保護対象が例えば3-4桁の数字であれば組み合わせは3桁で1,000通り、4桁で10,000しか存在しないため、元の値を知ることは簡単です。

ソルト

これを防ぐ技術がソルトです。ハッシュ関数は基本的に共通アルゴリズムです。つまりどのシステムでもハッシュ化すれば同じ文字列は同じハッシュ値を生成します。このアルゴリズムにシステム固有のアルゴリズムを含めるのがソルトです。一番簡単なものでいえばシステムで乱数を生成して元の文字列と連結した後ハッシュ値を生成します。

kameda harunobu + <乱数生成文字列>23440cd56316ad3b7e0df432e0858dad3b9cc436cdc5d79eec0a9af2a5fcadaa

こうすることで、このハッシュ値を再現できるのは、kameda harunobu とペアになっている乱数を知っているシステムだけ、ということになります。この処理において必ずソルトは毎回乱数生成された異なる値を使うことが必要です。

ちなみになぜソルト といわれるかといえばこれは塩が語源です。システムに対する調味料を加えることで異なった味付けをする、という意味です。

ソルトはどこに保存すべきか?

これは結構難しい問題です。同じデータベーススキーマやテーブルの中にソルトを保存しておくとハッシュ値と同時に漏洩します。このため一般的にはソルトはもう少し攻撃者が推察しづらいアルゴリズムを用いておく必要があります。こうすることで攻撃者がハッシュ値とソルトを入手したとしても簡単にレインボーテーブルを作れないようにします。

これは万が一ソースコードが漏洩しそのアルゴリズムも漏洩してしまったときにも、ある一定の対策となります。元の値を入手することはどのみちハッシュ化された文字列からは行えませんので、同じハッシュ値を生成する文字列を探す必要があります。攻撃者はソルトがあることで、あらかじめ持っているレインボーテーブルが使い物にならなくなるため、入手したアルゴリズムをもとに再度レインボーテーブルを再作成することになります。この計算量は膨大であり、なおかつ漏洩した文字列ごとに異なる(ハッシュ値生成に使われるソルトが文字列ごとに異なるため)テーブルが必要であるためその被害を最小限にとどめることが可能となります。

とはいえ、元の値が3~4桁の数字なら組み合わせは限られます。この場合sha-2 などの様に一般的なハッシュアルゴリズムではなく、やたらと計算に時間がかかるハッシュ関数を使う、という手法も存在しています。これにより攻撃者がレインボーテーブルを作成する時間を遅らせることができます。この手法は、正しいシステムにおける保護対象の文字列の利用の場面でも、通常より大きい計算リソースを消費することになりますので、パフォーマンス試験などは必須です。

またハッシュ化された値とソルトが同時に漏洩しないように、別の場所に保存するという対策も推奨されます。例えばAWSでシステムを構築するのであれば、ハッシュ化されたパスワードはAmazon RDS、ソルトはAmazon DynamoDBといった具合に分散させることでまとめて漏洩することを防ぎます。また、ソルトを保存するデータベースのカラム名に「salt」と設定することは絶対に避けましょう。

ペッパー

ソルトと組み合わせるペッパー という考え方もあります。ソルトは文字列ごとに乱数生成されますが、ペッパーはシステム全体の固有値でありコードに埋め込まれて利用されます。

kameda harunobu + ソルト<乱数生成文字列> + ペッパー<システム固有値> → ハッシュ値 となります。

この際、単純にソースコードに埋め込むより、AWS Secrets ManagerやHSM(Hardware Security Module)等に格納することで漏洩を防げます。少し高価ですが、情報漏洩時のマイナスインパクトを考えると十分な効果が見込めます。

このあたりの設計でサポートが必要でしたら、お気軽にご相談ください。

亀田治伸

認証系ASP、動画・音楽配信システム構築、決済代行事業者、暗号・認証商品ベンダーを経て現職。幅広いAWSのサービス群をソリューションとして構成したメッセージングを配信する傍ら、Blog、Web、カタログ、PPT等AWSのメッセージング管理作成業務を日々遂行。