【基礎知識】暗号化とは？ハッシュ化とは？パスワードの漏洩を防ぐには？

どうも@bayasistです。
休日に小中学生にプログラミングを教えているのですが、暗号化？ソルト？何それ？？？？って感じになったので、まとめてみます。いや、最近の中学生はすごいです。

# この記事の対象者

* ハッシュってなに？~~ハッシュドポテトは好きです~~という初心者の方
* ソルトって何？~~なぜ技術者向きの記事なのに塩？もしかして料理の記事？~~と思っている初心者の方
* 厳密さより分かりやすさを求める人
* 中学生でも、非エンジニアでも極力わかるように書いたつもりです

# 注意

* 当記事に出てきているパスワードは説明のために非常にシンプルになってます。実際にパスワードを決定する際にはもっと複雑なパスワードを設定しましょう。
* 当記事に出てきている暗号化・ハッシュ化の手法は説明用のために非常にシンプルなものを選んでます。よってセキュリティーレベルは最悪です。練習のために実装するのは構いませんが、実サービスでは絶対に使わないでくださいね。

# 暗号化って何だろう？
暗号っていうとどんなものを想像しますか？多くの皆さんは何かしら想像できるかと思います！
簡単に言えば、「あるルールに基づいて読みにくくした文章」のことを暗号と言いますね。
例えば次の文字は読めますか？

```
J bn Njlf.
```

さて、シンキングタイムです。。。

・・・
・・・
・・・
・・・

さて分かったでしょうか？

上記の文章を一文字一文字b→a、c→b、d→cとアルファベット順で一つ前のものにしてみましょう。

```
I am Mike.
```

ちゃんとした英語になりましたね！
「J bn Njlf.」のように工夫をして読みにくくした文章を暗号文と呼びます。


個人情報を保持しなければいけないときは、基本的には暗号化して保持しておきます。
暗号文が万が一漏洩した場合でも、その暗号を戻すためのルールが漏洩しなければ暗号を解読することは難しいので、セキュリティのリスクが軽減できます。
先ほどの例で出した暗号は、簡単に解けてしまった方がいるかと思いますが、実際にはもっともっと複雑なルールの暗号なので、元に戻すルールが漏洩しない限り、元の文章を復元することはかなり難しいです。

ということで、この章のまとめですが、

* 暗号は文章の解読を難しくしたもの
* 暗号文が漏れても元に戻すためのルールが分からないと、解読は難しい。

ということを覚えておいてください。


# 暗号化されたパスワードをもとに戻す必要性はある？

ショッピングサイトを想像してみてください。お客様から住所などを入力してもらいます。その住所は暗号化されて保存されますが、お客様の家に商品を届ける際に住所が必要なのでその時は暗号を解いて住所を調べる必要があります。

では次は会員制サイトを想像してみましょう。お客様がパスワードを決めて送信してくれたものを暗号化してデータベースに保存したとしましょう。ではその暗号化されたパスワードはもとに戻さなければいけない時ってあるでしょうか？
ログインの時に戻さないと認証できないのでは？と思った方は居るかもしれません。でも、暗号化された文字同士を比較すればログイン時の認証は実装することが可能なので、戻さなくてもいけない場面は存在しません。よくわかりくい説明になってしまったので下記の図を参照です。

![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/316506/7ae60684-2860-83f5-3030-d5a1c32aa331.png)

このようにパスワード認証の場合は、複雑化したパスワードを解読する必要がないのです。


# ハッシュ化って何だろう？暗号化との違いは？

前章で言ったように、パスワードにおいては暗号を解読する必要はありません。
ということは、そもそも解読できないようにしてしまえばいいのでは？という発想があるかと思います。

今度は各桁をすべて掛け合わせることで難読化することを考えましょう。「2468」であれば「2×4×6×8」で「384」です。
ではこの「384」という数字から逆に「2468」は導けるでしょうか？これは不可能です。

このような元に戻すことのできない難読化のことを「ハッシュ化」と言います。そしてハッシュ化によって生まれた値を「ハッシュ値」もしくは「メッセージダイジェスト」と呼びます。

パスワードを保存する際は、暗号化ではなくハッシュ化するのが一般的です。もとに戻すことが不可能の方がセキュリティが高いですから。

ちなみに、このケースでは「2468」のほかに「2648」、「2838」も「384」になります。「大丈夫なの？」と心配されそうですが現実世界ではもっと複雑なルールでハッシュ化されているため、このように複数の値から同じメッセージダイジェストが生まれることはめったにありません。(めったにないが極まれにある。)

# ソルトって何だろう？

パスワードはハッシュ化したし、万が一情報が漏れても大丈夫！と思うかもしれませんが、まだ不十分なのです。

例えば、Aさんのパスワードが「2468」、Bさんのパスワードも「2468」だとします。そうすると2人ともハッシュ化すると「384」ですね。もしこのAさん、Bさんのパスワードのメッセージダイジェストが「384」ということが漏れたらどうなるでしょうか？
これだけではAさん、Bさんの具体的なパスワードが何かはほとんどの人が分かりません。ただ、AさんとBさんのパスワードが一緒だということは分かってしまいます。その情報がAさんに知られてしまったら、AさんはBさんのパスワードが分かってしまいますよね。。。自分と一緒なんですから。またメッセージダイジェスト「384」は「2468」から出来ていると知っている人がいたら、パスワードを見抜くことが可能です。

そこでちょっと応用してみます。
ハッシュ化する前に、あるランダムな1桁の数字を先頭に付けてからハッシュ化します。そしてメッセージダイジェストの先頭に先ほどのランダムな数字を付けます。つまりパスワードが「2468」でランダムな数字を「3」とすると、「32468」をメッセージダイジェストにするので、「1152」となります。そこに再度ランダムな数字「3」を先頭に着けて「31152」で完成です。
Aさんはランダムな数字を「3」、Bさんはランダムな数字を「5」とします。そうするとAさんのメッセージダイジェストは「31152」、Bさんのメッセージダイジェストは「51920」となり、全然違う数字が出てきました！
また、メッセージダイジェスト「31152」はソルト「3」で「2468」から出来ていると知っている人がいれば、パスワードは推測できますが、今回は1桁の数字だったソルトをより複雑にしていけばこのような推測はかなり難しくなります。

ちなみにこのようにメッセージダイジェストを作る際に付けるランダムな値を「ソルト」と言います。

え、これでちゃんとログイン認証とかできるの？と疑問に思われるかもしれませんが、大丈夫です！

例えばAさんがパスワード「2468」でログイン認証してきた時を考えます。
Aさんの保存されているパスワードのメッセージダイジェストは『31152』で、最初の文字を見ると「3」なので、このメッセージダイジェストはソルトは「3」で作られたことが分かります。ユーザから送られて来たパスワード「2468」を、ソルト「3」でメッセージダイジェストを作ると「1152」で先頭にもう一度ソルト「3」を付けると『31152』なので確かに一致していることが分かります。

Aさんがパスワード「2469(パスワード間違い)」でログイン認証してきた時を考えます。
Aさんの保存されているパスワードのメッセージダイジェストは『31152』で、最初の文字を見ると「3」なので、このメッセージダイジェストはソルトは「3」で作られたことが分かります。ユーザから送られて来たパスワード「2469」を、ソルト「3」でメッセージダイジェストを作ると「1296」で先頭にもう一度ソルト「3」を付けると『31296』なので不一致でログイン失敗となるわけです。

# 実装してみよう

そのうち何か書くかもしれない。。。。

# 終わりに

いかがだったでしょうか？ハッシュ化という存在を知っていただき、暗号化とハッシュ化の違いが分かっていただければ、この記事の目的は達成したかなと思ってます。それではもし次回があればそれまで。。。