🔐

今さら聞けないハッシュ化

2021/02/16に公開

はじめに

今回はハッシュ化について紹介したいと思います。
情報系の職種に就いていると、意味はよく分からずともよく耳にする単語なので、なんとなく文脈から 「セキュリティ担保のためにパスワードを加工する何か」 のようにふわっと理解している方もいるのではないでしょうか。

ハッシュ化とは

ハッシュ化とは、元になるデータ(文字列)からハッシュ(あるいはハッシュ値)と呼ばれる文字列に不可逆変換することを指します。
ハッシュ化を行う関数をハッシュ関数と呼びます。

ハッシュ関数にもいくつか種類があり、有名どころだとMD5SHA-256,SHA-512,bcryptなどがあります。

用途としては後述の「パスワード」に関するものが多いですが、近年ではビットコインなどに用いられるブロックチェーンの分野でも扱われています。

どんな時に使うか

登場機会として一番多いのは 「パスワードの突合」 です。
ログイン機能を持ったサービスやアプリケーションでは、データベースにパスワードを保持しておき、ログイン時にユーザが入力したパスワードが正しいものかどうかを照合する必要があります。
その際にパスワードを元の文字列(平文)のまま保持しておくと、漏洩した場合などに大きなリスクとなります。

その際に「パスワードをハッシュ化した値」を保持しておくことで、漏洩した場合もパスワードの平文が流出することを避けることができます。
※ハッシュ値は不可逆のであり、 「ハッシュ値から元の値に戻すことはできない」 という特性があるため。

パスワードを突合する際は、「入力されたパスワードの平文から生成したハッシュ値」と「データベースに格納されているハッシュ値」を比較します。

暗号化との違い

文脈として「ハッシュ化」と「暗号化」が混同されたような使い方をされることがありますが、両者は別物です。
暗号化は可逆変換なので、「元のデータに戻すことができる」という特性があります。
対してハッシュ化は不可逆変換なので、「ハッシュ値から元の値に戻すことはできない」です。

脆弱性と対策

上記のような特性があるため、パスワードの管理などに用いられるハッシュ化ですが、万能というわけではありません。
いくら不可逆変換とはいっても、入力された値に対して出力されるハッシュ値は定まっているため、以下のように対応表を作ることで元の値を推測できてしまいます。

元の値 ハッシュ値
Zenn 12345
Google 67890
Twitter 24680
Facebook 13579
... ...

これは一般に「レインボーテーブル」と呼ばれる攻撃手法です。
こういった攻撃手法に対応すべく、ハッシュ化にはいくつかの工夫がされるのが一般的です。

ソルト

Salt(ソルト)はハッシュ化される前の文字列(平文)に付随される任意の文字列です。
例えば、パスワードをハッシュ化するにあたり「ユーザID」をSaltとして使うことで 「同じabcdefgというパスワードを設定しているユーザがいても、出力されるハッシュ値が異なる」 という状態にすることができます。
こうすることで先のレインボーテーブル上にpasswordadminといったよく使われるパスワードの平文とハッシュ値があったとしても、実際のパスワードの推測がしにくくなります。

ペッパー

Saltはその性質上、ハッシュ値と対応して管理されていることが多いです。
上記の例でいうところの、「パスワードのハッシュ値」と「ユーザID」はおそらく同じテーブルで管理されていることでしょう。
データが漏洩する場合、これらの値は同時に漏洩されることがほとんどのため、別な管理体系でSaltのような値を保持しているとより堅牢な仕組みになります。
それがPepper(ペッパーもしくはシークレットソルト)と呼ばれるものです。

Pepperはハッシュ化される文字列(平文)に付随される固定の文字列です。
Saltが平文ごとに異なるのに対してPepperは全ての平文に対して共通の値になります。
そのためデータベースに保持しておく必要がなく、アプリケーションの設定ファイルなどに組み込まれていることが多いです。

SaltPepperを組み合わせることでデータの漏洩に対してより強い仕組みとなります。

ストレッチング

ストレッチングは「平文」をハッシュ化してできた「ハッシュ値A」を再度ハッシュ化して「ハッシュ値B」を生成するように、複数回のハッシュ化を行うことです。
ストレッチングの回数を増やせば増やすほどレインボーテーブルを作成するのが困難になるため、ストレッチング回数は多い方が望ましいですが、その分処理のコストが嵩みます。
※一般的に10000回のストレッチングが推奨されているようです。

豆知識

以下、ハッシュ化に関する補足的な知識です。

パスワードの管理に向かないハッシュ関数がある

ハッシュ関数やアルゴリズムには有名無名さまざまなものがありますが、中にはパスワードの管理などには向かないものもあります。
例えば 「高速なハッシュ関数」 などです。
一般にアルゴリズムは高速であればあるほどよいとされますが、セキュリティ的な観点では高速なハッシュ関数は 「レインボーテーブルの生成も容易」 になってしまうためです。

平文とハッシュ値は完全に1対1ではない

入力となる平文は任意の長さであるのに対して、出力のハッシュ値は固定長であるため 「同じハッシュ値となる異なる平文の組み合わせ」 が理論上存在します。
これは 「衝突」 と呼ばれるもので、近年有名なものでは2017年にGoogleSHA-1の衝突を発表しました。
https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html

まとめ

今回は「ハッシュ化」および「ハッシュ値」について紹介しました。
普段何気なく行っているログインなどの裏側で利用されており、非常に身近な技術になります。

セキュリティの分野で仕事をされている方には比較的馴染みがありますが、自分自身普段はフロントエンドを中心に扱っているため、知っているようで知らないというのが正直なところでした。
記事にすることで体系的にまとめられたので、備忘録としても役立つのではないかなと思います。

今回は実装には触れませんでしたが、いずれJavascriptTypescriptで主要なハッシュ関数を用いた実装を紹介したいと思います。

Discussion