Python salt stretching ソルトとストレッチング

Python学習【365日チャレンジ!】238日目のマスターU(@Udemy11)です。

ネットの広告に出てきた「年間120時間の時短を実現した50のテクニック 脱マウス最速仕事術」を9月頭に購入して実践しているところですが、どうしても右手がマウスに言ってしまいます。

MacBookProを使うときは普通にタッチパッドに親指や中指がいってしまうんですよね。

あと、Pycharmでは、vimを使ってコードを打つようにしているのですが、インサートモード、ノーマルモード、ビジュアルモードの使い分けがいまいちうまくできません。

プログラマーではないので、圧倒的に使用頻度が少ないため、キーの役割をいちいち考えてしまうので、ブラインドタッチのように操作することができていません。

vimは、技術習得までのラーニングコストが高いといわれているので、そんな簡単にマスターできるとは考えていませんが、操作に慣れるまでにちょっと時間がかかりそうです。

どうしても今までやっていた方法が一番操作しやすいので、そちらに逃げてしまい、結局vimを使うのを挫折する人がいるのもうなずけます。

vimをマスターするには、かなり苦労すると思いますが、vimを自在に操れるPythonマスター目指して使い続けたいとおもいます。

昨日の復習

昨日は、hashlibを使った暗号化を学習しました。

hashlibを使って任意の文字列をハッシュ化してシステムのログインに使いましたが、ハッシュ化は暗号化と違い、ハッシュ値から元の値に復元する復号化ができないのが特徴でした。

文字列から作られるハッシュ値は、元の値が同じならハッシュ値も同じになるので、対応表があれば復元可能でした。

詳細については、昨日の記事をごらんになってみてください。

今日は、hashlibを使ったよりセキュリティの高い暗号化を学習します。

salt

昨日学習したように、任意の文字列のパスワードから作られるハッシュ値は、もとの値が同じなら、同じハッシュ値になるので、ありとあらゆる値をハッシュ化した対応表があれば、ハッシュ値からパスワードがわかってしまいます。

そこで使われるのが、元のパスワードにランダムな文字列を付け足してハッシュ化する方法です。

この方法だと、万が一データベースが漏洩してしまったとしても、ハッシュ値から元の値が解読できたとしても、その値はパスワードじゃないので、単純なハッシュ化よりもセキュリティーが高くなります。

それがsaltというもので、元のパスワードの前か後ろに付け足してハッシュ化します。

import base64
import os
import hashlib

user_name = 'user1'
user_pass = 'zyxabcde'
db = {}

salt = base64.b64encode(os.urandom(32))

print(salt)

def get_digest(password):
    password = bytes(user_pass, 'utf-8')
    digest = hashlib.sha256(salt + password).hexdigest()
    return digest

db[user_name] = get_digest(user_pass)

def is_login(user_name, password):
    return get_digest(password) == db[user_name]

print(db)
print(is_login(user_name, user_pass))

ハイライトしているprint出力は、saltdbに何が入っているのかわかるように出力しています。

最初にsaltのランダムな値を作るためにbase64osをインポートします。

9行目は、saltを作成するときの定番のコードなので、覚えておいて損はありません。

作成したsaltは、15行目のハッシュ値digestを作成する際に、passwordに付け足して使います。

生成したハッシュ値を18行目でuser_namevalueに代入します。

あとのコードは昨日と同じで、dbに保存された値とuser_nameuser_passを照らし合わせてTrueFalseを返しています。

出力結果

b'pinbUUFOgQPTSG/rNHGeF8ZgZN+7eELXsmwjm3vyejU='
{'user1': '502e946cc1ed08d7eea9944a81957295edf3428cc21ff01cb3eaf55ccd5e1b94'}
True

このコードの場合、dbに保存した際のsaltと仮のログインで参照するsaltが同じなので、結果はTrueになっています。

実際は、データベースにuser_nameとハッシュ化されたuser_passを保存する段階で、生成されたsaltも同じデータベースか他のデータベースに保存しておく必要があります。

でないと、ログインの際に、ハッシュ値が違ったものになってしまいますので、注意が必要です。

stretching

saltを使ったハッシュ化は、ハッシュ化のみよりもセキュリティーが高まりますが、データベースに保存したsaltも一緒に漏洩してしまうと、パスワードが解読される可能性があります。

そこで登場するのがハッシュ化を複数回繰り返してハッシュ値を生成するストレッチングです。

forループを任意の回数繰り返してハッシュ値を作るので、この値を復元するのは不可能に近くなります。

def get_digest(password):
    password = bytes(user_pass, 'utf-8')
    digest = hashlib.sha256(salt + password).hexdigest()
    for _ in range(10000):
        digest = hashlib.sha256(bytes(digest, 'utf-8')).hexdigest()
    return digest

13行目の関数に16行目、17行目のforループのコードを付け足しています。

passwordsaltを付け加えて作成したdigestを元にforループで10000回ハッシュ化した値に変更しています。

ストレッチングによって、ハッシュ値からハッシュを作るという作業を10000回も実行しているので、ハッシュ値が漏洩したとしても元の値を求めるにはほぼ不可能に近くなります。

まとめ

守る側と盗む側のやり取りは、基本的にいたちごっこです。

守る側はあらゆる攻撃に備えないといけませんが、盗む側は一つ穴を見つければそこから攻めることができます。

当然、守る側より盗む側(攻める側)のほうが有利ですし、攻めるほうは守るものがないので、捨て身で攻撃することができます。

守る側は、すべての穴を塞げるわけではないので、可能な限り攻撃に備えないといけませんが、コストも膨れ上がるので、セキュリティーと費用対効果のトレードオフになるのが現状です。

プログラマー側としては、できる限りのセキュリティー対策はしっかりとやっておかないといけないので、ハッシュ化や暗号化・復号化についてはしっかりと学習しておきましょう。

それでは明日もGood Python!