カネキン伊藤テック

ハッシュ値の計算

Pythonでハッシュアルゴリズムを用いて特定のバイト列からハッシュ値を計算するには組み込みライブラリ hashlib を利用する。

hashlib.md5()hashlib.sha1()hashlib.sha256()hashlib.sha512() などさまざまなアルゴリズムが同じインタフェースで利用できる。

文字列に対するハッシュを得る

ハッシュというのはバイト列に対して計算するものなので文字列( str )型は bytes 型にする必要があるので encode() が必要。(ここでは UTF-8 でエンコードしたバイト列にしている)

import hashlib

def compute_hash(target_str, encoding="utf-8"):
	target_bytes = target_str.encode(encoding)
	
	hash = hashlib.md5(target_bytes)
	return hash.hexdigest()  # 返り値は str 型( "acd78951602a8cbc0cae289ceb6e579f" のような)

compute_hash("こんにちは")  # => "c0e89a293bd36c7a768e4e9d2c5475a8"

↑の例では hexdigest() で16進数文字列でハッシュ値を得ているが、 digest() メソッドを使えばバイト列で得ることも可能。

import hashlib

hash_result = hashlib.md5("こんにちは".encode("utf-8"))
hash_bytes = hash_result.digest()
print(hash_bytes)  #=> b'\xc0\xe8\x9a);\xd3lzv\x8eN\x9d,Tu\xa8'

ファイルのハッシュを得る

大きなファイルにも対応できるようにするには少しずつ読み込んでハッシュを計算していく。 bufsize は実行環境によっては大きくしたり小さくしたりすると実行速度が改善されると思うので計ってみて良い値を使う。

import hashlib

with open("file.txt", "rb") as fh:
	hash = hashlib.md5()  # sha1() とか sha256() とか sha512() もある
	bufsize = 1024 * 64
	while True:
		buf = fh.read(bufsize)
		if buf == b"":
			break
		hash.update(buf)
	# return hash.digest()  # 返り値は bytes 型
	return hsah.hexdigest()  # 返り値は str 型

パスワードのハッシュを得る

認証に用いるパスワードはハッシュ化して保持することでセキュリティを高めようというのは広く知られているが上述のような単純なハッシュ値を得るような処理ではレインボーテーブル攻撃に対して脆弱であり推奨されない。 ストレッチング(ハッシュ処理を何度も行う)やソルト(個別にランダムな文字列を付与)を組み合わせて万が一パスワードをハッシュ化したハッシュ値が漏洩しても全てのパスワードがクラックされる可能性を計算量的に難しくするセキュリティアプローチが一般的であり、このような汎用的な処理を実装したライブラリが werkzeug パッケージにあるのでこちらを使うのがよい。

werkzeug パッケージは組み込みではなくPyPIパッケージなので pip でインストールが必要。

import werkzeug.security  # pip install werkzeug が必要

# 生のパスワードからDBに保存するパスワードハッシュを得る
raw_pass = "あいうえお"
hash = werkzeug.security.generate_password_hash(raw_pass)
# 戻り値の文字列 hash にはアルゴリズム, salt値, ストレッチング回数などの情報がふくまれている

# 保存されていたハッシュをもとに, 入力された値が正しいパスワードかを検証
input_pass = "あいうえお"

if werkzeug.security.check_password_hash(hash, input_pass):
	print("正しいパスワード")
else:
	print("間違ったパスワード")

参考リンク