やるだけPython競プロ日誌

競プロの解説をPythonでやっていきます。できるだけ初心者に分かりやすいように『やるだけ』とかは言わないようにします。コメントについては必ず読んでいます。どんなに細かいことでもいいのでコメントくださればうれしいです。

国会でも話に上がった "HASH" をPythonで -- hash()じゃないよ! --

以前、”国会にhash関数の話が出たwww”といって、そっちの界隈の人でちょっとした話題になっていました。

では、hashとはなんなのか?分からない人もいると思います。

それがわかっても、Pythonでの実装がわからない方もいらっしゃると思いますので、この記事を書いた次第です。

hashって?

wikipediaを参照すると

データから算出した小さな値。各データを区別・表現する目的に用いる。

となっています。

つまり、あるデータから、それ固有の値を取り出して(計算から導き出す)、データの区別に使うための値。です

この計算というのが大事で、たとえば、長さが400ページ以上もある本を文字に落とし込んで所得したhashと、

その文字列の句点と読点を一個だけ入れ替えた文字列のhashは異なります

そこから、文書の改ざん対策などにも用いられるんですね。

また、その『元からhashを求めるのは用意である』が『hashから元を求めるのは非常に困難である』という性質を利用して、暗号・セキュリティにも用いられています。

Pythonで実装

Pythonにはhash関数が元からありますので、それを利用してみましょう。

>>> hash("1")  # "ハッシュ不可能"(listやset, dictなど)は用いないでください
4940519975841726253

求められましたね。

しかし、これではだめです

このhashが信頼できるのは、このインタープリタを閉じるまでだからです。

一度閉じて、もう一度実行してみましょう。

>>> hash("1")
2261270426266027010

おなじ文字列のhashを取り出したにもかかわらず、違う値が出ていますよね。

このように、正当性を確かめるには、標準のhashは不向きであるとわかります。

そこで使うのが、hashlibです

>>> import hashlib
>>> hashlib.md5(b"1").digest()  # bytesにしましょう
b'\xc4\xcaB8\xa0\xb9#\x82\r\xccP\x9aou\x84\x9b'
  # 一度閉じます
>>> import hashlib
>>> hashlib.md5(b"1").digest()
b'\xc4\xcaB8\xa0\xb9#\x82\r\xccP\x9aou\x84\x9b'  # 同じ値が返ってきます

みなさんが同じコードを打っても、同じ値が返ってくるはずです。

これが、一意性を保っているということです。

MD5(hash化の方法の種類です)のほかに、SHA1、SHA224、SHA256、SHA384、SHA512が対応しているようです。

ユーザーのパスワードをhash化して持っておき、ログインしようとしたときに、そのhashと入力された文字列のhashを比べる、なんてこともできますよね。
MD5に関しては、衝突(別のデータが同じhash値を持つ)ことが他のものと比べて多いようなので、注意です


しかし、まだまだパスワードは軟弱で、Brute-Force-Attack(総当たり攻撃)に対抗するだけの力がありません。

なので、Salt(パスワードと関係のない値)と反復を繰り返すのがhashlib.pbkdf2_hmac()です。

より高度な使用

>>> import os, hashlib, binascii
>>> salt = os.urandom(64)  # osの提供する64文字長の信頼できるrandomなbytes
>>> salt
b'\xc0<U\xdc5\xea\x8d+y\xe6W\x882U\xdaG\xbe\xd5\x19\xc0\xb2l\xf5\xd5/~W2\xa9\x92z\x8b\xff\xba\x8bs\x95t)I\xd1s\xfa.\x85R\xff\xd6\x89\x10n\xb7\xc3<\x84\x87\xea\xe6X\x8a\xdb,\xc3\x1c'
>>> dk= hashlib.pbkdf2_hmac("sha256", b"PaSsWoRd", salt, 100000)  # sha256でb"PaSsWoRd"とsaltを使って100000回hmacの反復
>>> binascii.hexlify(dk)  # 得られたhash
b'53ce88a1559e955b7b68794225365af09261f0a411629cef554de208e33e8da7'

といった感じです。

最低反復は 10,000回以上回すことが推奨されています。

それでは皆さん、よいPythonLifeを!!