キーワード

パスワードの守護神「ハッシュ化」の歴史をコードで旅しよう! –おつまみとしてのソースコード第14皿

ユニークで面白い、意外なアイデアが詰め込まれているソースコードの世界をおつまみ感覚で紹介するのが、情シスのじかんの「おつまみとしてのソースコード」です!週末も近づき、ちょっと一息つきたくなる木曜日の夕方にお届けしていきます。

「ユーザーのパスワードをリセットしました」――。

パスワードリセットは、情シスの日常的な業務のひとつですよね。ところで、そのパスワードがデータベース上でどのように保管されているか、改めて問われるといかがでしょうか。

もちろん、パスワードを守る「ハッシュ化」が基本であることは、皆様よくご承知のことと思います。

ただ、一口に「ハッシュ化」と言っても、その技術は時代と共に大きく変化してきました。今回は、その進化の歴史に焦点を当てていきます。

おつまみとしてのソースコードの第十四回は「ハッシュ化の歴史」。パスワードを守るための基本的な技術「ハッシュ化」の歴史を、簡単なPythonコードと共に旅します。過去のハッシュ化の方法から現代の標準的な方法まで、進化の過程を追いかけていきましょう。

エンジニア仲間とのアイスブレイクや、一人でまったりする時間のお供に、ぜひご覧ください!

         

そもそもハッシュ化とは?

ハッシュ化とは、あるデータを元に戻せない別のデータに変換する技術です。例えば、「password123」という文字列から「e10adc3949ba59abbe56e057f20f883e」のような、一見ランダムな文字列である「ハッシュ値」を生成します。同じ入力からは必ず同じハッシュ値が得られるのが特徴です。

これにより、システム管理者は元のパスワードを知なくても、ユーザーが入力したパスワードが正しいかについて、ハッシュ値が一致するかを検証することで確認できます。

ハッシュ値生成の仕組み

ハッシュ値生成の仕組みについても少し触れておきましょう。ハッシュ値の計算プロセスは、主に以下の3ステップで構成されています。

・ステップ1:データのブロック分割
まず、入力されたデータを、アルゴリズムが処理できるように決まった大きさの「ブロック」に均等に分割します。長さが足りない場合は、決まったルールでデータを付け足し(パディング)、サイズを揃えます。

・ステップ2:連鎖的な圧縮計算
ここがハッシュ化の心臓部です。分割したブロックを、圧縮関数と呼ばれる計算ロジックを使い先頭から順番に処理していきます。圧縮関数は、データのブロックと「一つ前の計算結果」をもとに、ビットの入れ替えや数学的な足し算など、複雑な計算を行います。この計算結果が、次のブロックを処理する際の新たなインプットとなり、処理が連鎖していきます。この連鎖的な仕組みにより、入力データが1ビットでも違うと、最終結果が予測不能なほど大きく変わります。

・ステップ3:最終ハッシュ値の出力
すべてのブロックを処理し終えた最後の計算結果がハッシュ値です。SHA-256なら256ビットといった、アルゴリズムごとに決まった固定長のハッシュ値となります。

黎明期――速すぎた英雄「MD5」

ハッシュ化が普及する以前、パスワードは平文のまま、あるいは容易に元に戻せる暗号化を施しただけで保存されているシステムも少なくありませんでした。

そんな中、1991年に暗号学の権威であるロナルド・リベスト氏によって開発されたのがMD5です。パスワードを元に戻せないハッシュ値に変換して保存するこの方法は、データベースが盗まれてもパスワードそのものは漏洩しない、画期的なセキュリティ向上策でした。

コードで見るMD5

では、かつて広く使われたハッシュ関数「MD5」について、Pythonのコードで試してみましょう。

・ソースコード

・実行結果

MD5の致命的な弱点

ハッシュ化の登場は画期的でしたが、MD5には最大の弱点がありました。それは計算速度が速すぎたことです。

MD5は、現代のコンピュータなら、1秒間に何十億回ものハッシュ計算が可能です。そのため、よく使われるパスワードのハッシュ値を事前に計算してリスト化しておく「レインボーテーブル攻撃」が非常に有効になってしまいました。

攻撃者は盗んだハッシュ値とリストを照合するだけで、元のパスワードを瞬時に特定できてしまうのです。

変革期――「ソルト」の登場

同じパスワードからは同じハッシュ値が生成される。ならば、ハッシュ化する元の文字列をユーザーごとに変えてしまえばいい! という発想を実現するのがソルト(Salt)です。ソルトとは、パスワードに付加するユーザー固有のランダムな文字列のことです。

パスワード + ソルト → ハッシュ化

この方法により、たとえ2人のユーザーが同じ「password123」を使っていても、ソルトが違うため、保存されるハッシュ値は全くの別物になります。これが、レインボーテーブル攻撃への対策となるのです。

コードで見る「ソルト付きハッシュ」

MD5より強力なハッシュ関数「SHA-256」とソルトを組み合わせてみましょう。

・ソースコード

・実行結果

残された課題

ソルトはレインボーテーブル攻撃を無力化する、大きな進歩でした。しかし、もしデータベースそのものが漏洩した場合、攻撃者の手には、ユーザー名、ソルト、ハッシュ値の3点セットが渡ってしまいます。

そうなると攻撃者は、特定のユーザーに狙いを定め、そのユーザーのソルトを使って、ブルートフォース攻撃(総当たり攻撃)を仕掛けることができてしまいます。SHA-256のようなハッシュ関数は、この一対一の攻撃に対しては計算が速すぎた、というのが残された課題でした。

現代――「わざと計算を遅くする」という逆転の発想

攻撃者の最大の武器が計算速度なら、それを奪ってしまえばいい。この逆転の発想から生まれたのが、パスワード専用に設計されたハッシュ関数です。代表的なものにbcryptがあります。

bcryptは、意図的に計算に時間がかかるように作られています。さらに、「コスト係数」を設定することで、その計算量を調整できるのが最大の特徴です。将来コンピュータが高速になっても、コスト係数を上げることで、ハッシュ化にかかる時間を一定に保ち、安全性を維持できるのです。

コードで見るbcrypt

Pythonのbcryptライブラリを使ってみましょう。ソルトの生成も自動で行ってくれます。

・ソースコード

※bcryptはPythonに標準で含まれているライブラリではないため、初めて使用する際には「pip install bcrypt」コマンドで別途インストールを実行してください。

・出力結果

現在のスタンダード

上記のコードで生成されたbcryptハッシュ値は、実行ごとに変わります。bcryptの出力結果には、以下の情報が含まれています。

・$2b$:bcryptのバージョン
・$12$:コスト係数(2の12乗回)
・z0hOldsQ8QsbfRHDXY5G0e:ソルト(22文字)
・ncq90… 3u:ハッシュ本体(31文字)

ハッシュ値自体が検証方法(アルゴリズム、コスト、ソルト)の情報を持っているため、この情報のみでパスワードの検証が完結します。bcryptや、さらに後発のArgon2といった「意図的に遅くする」パスワード専用ハッシュ関数を使うことが、現在のスタンダードとなっています。

まとめ

パスワード保護の歴史は、攻撃者の「計算速度」との戦いの歴史だったといえるでしょう。私たちが現在使っているハッシュ化は、過去の戦いから生まれた技術の上に成り立っているのです。

次にシステムを検討・評価する機会があれば、パスワード保管の方式について、その歴史に思いを馳せながら、詳しく確認してみてはいかがでしょうか。

著者:羽守ゆき
大学を卒業後、大手IT企業に就職。システム開発、営業を経て、企業のデータ活用を支援するITコンサルタントとして10年超のキャリアを積む。官公庁、金融、メディア、メーカー、小売など携わったプロジェクトは多岐にわたる。現在もITコンサルタントに従事するかたわら、ライターとして活動中。
 
 

特集|仕事で疲れた脳みそをリフレッシュ♬
おつまみとしてのソースコード

木曜日の夕方に仕事で疲れた脳みそをリフレッシュしたいとき、一人でゆっくりしたい夜、ちょっとした空き時間に、気軽に「つまめる」ソースコードの話題をお届けします。

まるで隠れ家バーでマスターが語るウンチクのように、普段は見過ごしがちなコードの奥深さや、思わず「へぇ〜!」と唸るようなユニークなアイデア、クスッと笑える小ネタを、ソースコードの世界を熟知した「情シスのじかん」がご紹介します。

コードを書くのが大好きなエンジニアさんも、ソースコードはちょっと苦手…という情シス部門の方も、この特集を読めば、きっとソースコードの新たな一面を発見できるはず。

業務効率化のヒントになるTipsや、セキュリティ対策に役立つ情報も盛りだくさん。

さあ、あなたも「おつまみとしてのソースコード」で、技術の世界をもっと身近に、もっと楽しく感じてみませんか?

本特集はこちら
 

関連記事Related article

       

情シスのじかん公式InstagramInstagram

       

情シスのじかん公式Instagram

30秒で理解!フォローして『1日1記事』インプットしよう!

掲載特集

close close