ハッシュ関数とハッシュ値とは?特徴もわかりやすく解説!
こんにちは!
現役Webエンジニアの今井(@ima_maru)です。
今回は、様々な用途で使われる「ハッシュ」について解説します。
- ハッシュとは
- ハッシュ値とは
- ハッシュ関数とは
- ハッシュ値が生成される流れ
- ハッシュ関数・ハッシュ値の特徴や性質
- ハッシュ関数・ハッシュ値の用途
今話題の「ブロックチェーン技術」の仕組みにもかかわっている重要な概念です。
初めに断っておきますが、ハッシュ関数自体の詳しい仕組み、アルゴリズムには触れません。
ご了承ください。それでは解説していきます。
ハッシュ(hash)とは?
ハッシュとは、日本語では「細切れ」といった意味があります。
- ハッシュドポテト(hashed potato)
- ハッシュドビーフ(hashed beef)
もっと言えば、「寄せ集め」という言葉が当てはまります。
しいて言うなら、「データをぐちゃぐちゃにして寄せ集める」といったようなイメージです。
IT用語では、「ハッシュ」というより、「ハッシュ関数」「ハッシュ値」「ハッシュ化」といったような熟語で使うのが一般的のように思います。
ハッシュ関数とは?
ハッシュ関数とは、任意のデータから規則性のない固定長のビット列を生成する関数です。
もっとかみ砕いていえば、ある計算によって入力データから決められた長さの出力データを得る関数です。
有名なハッシュ関数でいえば「MD5」「SHA-1」「SHA-2」などがあります。
ここでは、「ふーん」程度で構いません。
後ほど詳しく解説します。
ハッシュ値とは?
ハッシュ値とは、ハッシュ関数から生成された固定長のビット列・値のことを指します。
要は、ハッシュ関数から生成される値のことです。
ハッシュ化とは?
ハッシュ化とは、ある入力データをハッシュ関数に入力しハッシュ値にすることを指します。
例えば、「パスワードをハッシュ化する」というような使われ方をします。
それでは、ハッシュ関数からハッシュ値が生成される流れを見てみましょう。
ハッシュ関数からハッシュ値が生成される流れ
まずは、「ハッシュ値」を生成するために必要な「ハッシュ関数」というものを用意します。
今回は、C++の標準ライブラリの「hash」を使います。
hash<string> hash_function;
これがC++の文字列用のハッシュ関数です。
hash_functionを名付けました。
気になった方は、「SHA-2」などと検索してみてください。
次に、実際にデータからハッシュ値を生成する処理です。
size_t hash_value = hash_function(data);
このhash_functionの引数にデータを渡すと、ハッシュ値が返ってきます。
このハッシュ値をhash_valueと名付けることにします。
では、適当な文字列をデータとしてハッシュ関数に渡してみましょう。
ハッシュ関数からハッシュ値を得る
まずは、以下のような文字列のデータからハッシュ値を生成してみましょう。
int main()
{
// データ
string data = "吾輩は猫である。名前はまだ無い。";
// ハッシュ関数
hash<string> hash_function;
// ハッシュ値の計算
size_t hash_value = hash_function(data);
// ハッシュ値の表示
cout << bitset<40>(hash_value) << " : " << hash_value << endl;
return 0;
}
そしてその文字列をハッシュ関数に通して、ハッシュ値を生成してみましょう。
そうすると、
0000000000101011101110101111100110110000 : 733673904
このようなハッシュ値が生成されます。(左が2進数表現40bit分で右が10進数表現です。)
同じデータは同じハッシュ値を生成する
次に、もう一回同じ文字列をハッシュ関数に渡したらどうなるのかを試してみましょう。
int main()
{
// データ
string data1 = "吾輩は猫である。名前はまだ無い。";
string data2 = "吾輩は猫である。名前はまだ無い。";
// ハッシュ関数
hash<string> hash_function;
// ハッシュ値の計算
size_t hash_value1 = hash_function(data1);
size_t hash_value2 = hash_function(data2);
// ハッシュ値の表示
cout << bitset<40>(hash_value1) << " : " << hash_value1 << endl;
cout << bitset<40>(hash_value2) << " : " << hash_value2 << endl;
return 0;
}
どちらも同じ値ですが、どうなるでしょう。
生成されたハッシュ値を見てみましょう。
0000000000101011101110101111100110110000 : 733673904 // 1回目
0000000000101011101110101111100110110000 : 733673904 // 2回目
まったく同じハッシュ値が生成されていますね。
これらのことから、ハッシュ関数はどうやらランダムな処理ではないということがわかりますね。
入力データに対し、あるきまった処理をしていると予想がつきます。
このように、ハッシュ関数は、同じ入力データに対しては同じ出力を返します。
似ているようなデータでも全く異なるハッシュ値を生成する
では、つぎは似たような文字列データを入れてみましょう。
int main()
{
// データ
string data1 = "吾輩は猫である。名前はまだ無い。";
string data2 = "吾輩は犬である。名前はまだ無い。";
// ハッシュ関数
hash<string> hash_function;
// ハッシュ値の計算
size_t hash_value1 = hash_function(data1);
size_t hash_value2 = hash_function(data2);
// ハッシュ値の表示
cout << bitset<40>(hash_value1) << " : " << hash_value1 << endl;
cout << bitset<40>(hash_value2) << " : " << hash_value2 << endl;
return 0;
}
「猫」の部分を「犬」にしてみました。
文字列的には一文字違いという些細な違いですね。
そうすると、やはりハッシュ値も「少しぐらい」異なるのでしょうか?
生成されたハッシュ値を見てみましょう。
0000000011111111011001110101101110101100 : 4284963756 // 猫
0000000011011101000010001010110101100010 : 3708333410 // 犬
生成されたハッシュ値は「全くといっていいほど違う値」になりました。
このように、ハッシュ関数は、似たような値でも少しでも異なれば全く異なるハッシュ値を生成します。
ハッシュ関数は入力データの大きさによらず固定長の出力を返す
次は、先ほどよりもいくら長い文字列データを入れてみることにします。
以下のような長文を入れてみることにしました。(コード略)
吾輩は猫である。名前はまだ無い。どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。この書生というのは時々我々を捕えて煮て食うという話である。しかしその当時は何という考もなかったから別段恐しいとも思わなかった。ただ彼の掌に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始であろう。この時妙なものだと思った感じが今でも残っている。第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶だ。その後猫にもだいぶ逢ったがこんな片輪には一度も出会わした事がない。のみならず顔の真中があまりに突起している。そうしてその穴の中から時々ぷうぷうと煙を吹く。どうも咽せぽくて実に弱った。これが人間の飲む煙草というものである事はようやくこの頃知った。
これを入れてハッシュ値を計算してみました。
0000000011111111011001110101101110101100 : 4284963756 // 短文
0000000001001110001110110110001001111100 : 1312514684 // 長文
短文でも長文でもビット幅が足りないということはなさそうですね。
結果から言えば、これまでのハッシュ値はすべて、32ビットで収まっています。
これから考えるに、C++のハッシュ関数は固定長が32bitのハッシュ値を返すアルゴリズムになっていそうですね。
このように、ハッシュ関数は、あらかじめ決められた固定長の出力を返します。
「ハッシュ関数」「ハッシュ値」の関係性
ここで、もう一度「ハッシュ関数」「ハッシュ値」の関係を確認しておきましょう。
「ハッシュ関数」:ある計算によって入力データから決められた長さの出力データを得る関数
「ハッシュ値」:ハッシュ関数から生成される値
この意味が理解できましたでしょうか。
ざっくりとで大丈夫だと思います。
ここからは、「ハッシュ関数」「ハッシュ値」にはどんな性質があり、どんなふうに使われているのかを紹介しようと思います。
まず先に、「ハッシュ関数、ハッシュ値にどんな性質があるのか」からです。
ハッシュ関数の特徴・性質3つ
ハッシュ関数には、とても面白い性質があります。
この性質はいろいろな用途へと利用され、現在のセキュリティ技術などに貢献しています。
ここでは、ハッシュ関数の3つの性質をご紹介します。
- 入力するデータが少しでも違うと全く異なるハッシュ値を生成する
- 入力するデータの大きさにかかわらず一定の長さのハッシュ値を生成する
- 任意のハッシュ値を生成するのが困難
それぞれ紹介します。
入力するデータが少しでも違うと全く異なるハッシュ値を生成する
ハッシュ関数の性質その①は、
「入力するデータが少しでも違うと全く異なるハッシュ値を生成する」ということです。
これはさきほど「猫」と「犬」で確認した通りです。
もっといえば、ある1ビットが反転していただけでも、生成されるハッシュ値は全然違います。
このように、少しの違いでもハッシュ値には大きな影響を及ぼします。
入力するデータの大きさにかかわらず一定の長さのハッシュ値を生成する
ハッシュ関数の性質その②は、
「入力するデータの大きさにかかわらず一定の長さのハッシュ値を生成する」ということです。
これはさきほどの長文で確認した通りです。
長い文字列でも、短い文字列でも、一定の長さのハッシュ値を生成します。
今回用いたC++のhashは、32bitでしたね。つまり、
「00000000 00000000 00000000 00000000」~「11111111 11111111 11111111 11111111」
32bitの出力です。(正確に確認してはいないですがそうだと思います。)
どんなに大きな値を入れようとも、ハッシュ値はこのなかに収まります。
これは、大容量のデータでも同じことが言えます。
なので、ハッシュ値を計算すると、圧倒的にコンパクトなbit列に収まるというメリットがあります。
任意のハッシュ値を生成するのが困難
ハッシュ関数の性質その③は、
「任意のハッシュ値を生成するのが困難」ということです。
言い換えれば、「どんなデータならばこのハッシュ値になるだろうか」という予想がつかないということです。
この性質が、セキュリティの技術に活きてきます。
ハッシュ関数の用途3つ
ハッシュ関数の用途は多岐にわたります。
今回は、主なハッシュ関数の用途3つを紹介します。
- 暗号化技術・セキュリティ
- データの検証用・整合性チェック
- ハッシュテーブル
暗号化技術・セキュリティ
ハッシュ関数の用途として、暗号化技術があります。
主に「改ざんされているかいないかの確認」です。
例えば、今話題の「ブロックチェーン技術」、情報セキュリティでは欠かせない「電子署名」といった技術にもそのような用途で使われています。
データの検証用・整合性チェック
別のハッシュ関数の用途として、データの検証があります。
例えば、大容量かつとても重要なデータをダウンロードしてくるときに活躍します。
私が普段使う例でいえば、OSのダウンロードです。
OSというのは、パソコンを動かすとても重要なソフトウェアであり、データの欠損やウイルスの混入といったことは許されません。
しかも結構容量が大きいです。
私は、OSのダウンロードが終わり次第、ダウンロードしたデータのハッシュ値を計算します。(そういう機能があります。)
そして、計算したハッシュ値を、公式サイトで掲載されているハッシュ値と見比べます。
これで、その値があっていれば、ちゃんとダウンロードできたということになり、違っていれば公式サイトのデータとはどこかが異なっているということになります。
これは、ハッシュ関数の持つ「入力するデータの大きさにかかわらず一定の長さのハッシュ値を生成する」という性質のおかげです。
つまり、大容量のデータを超シンプルに表した「ハッシュ値」という存在のおかげでダウンロードがしっかりできたか否かを判別できるのです。
ごくまれに、違うデータでもハッシュ値が被ること(ハッシュ値の衝突)がありますが、こういった場面では気にならないほどです。
ハッシュテーブル
別のハッシュ関数の用途として、ハッシュテーブルという技術があります。
これは少し難しいのですが、データ検索の高速化の技術とでもいえばよいでしょう。
まとめ
今回は、「ハッシュ」というワードについて解説しました。
まず初めに、ハッシュの意味や「ハッシュ関数」「ハッシュ値」といったワードの関係性を紐解きました。
- ハッシュの日本語になおすと「細切れ」や「寄せ集め」といった意味。
- ハッシュ関数とは、ある計算によって入力データから決められた長さの出力データを得る関数である。
- ハッシュ値とはハッシュ関数から生成される値である。
次に、ハッシュ関数の性質という側面に注目しました。
- 入力するデータが少しでも違うと全く異なるハッシュ値を生成する
- 入力するデータの大きさにかかわらず一定の長さのハッシュ値を生成する
- 任意のハッシュ値を生成するのが困難
最後に、ハッシュ関数がどんなところに使われているのかという側面に注目しました。
- 暗号化技術・セキュリティ
- データの検証用・整合性チェック
- ハッシュテーブル
ここまでで解説を終わります。
以上「ハッシュ関数とハッシュ値とは?用途や性質もわかりやすく解説!」でした!
最後までご覧いただきありがとうございます。