2014.08.22   |   Python

Hash関数まとめ(md5, sha1, sha2, crc32, PBKDF2)

ハッシュの16進表記にした時の長さをまとめておこうと思ったら、
いろいろメモしておく内容が出てきたのでまとめてみました。

md5

  • ビット長: 128
  • 16進表記の長さ: 32文字
  • 最もメジャーなハッシュ関数
  • 2つのファイルが異なる事を十分チェックすることができる
  • いろいろな実装系の標準で関数・ライブラリが用意されている
  • いざとなれば自分でも実装できる
  • 計算コストも割と低め
  • セキュリティとしては脆弱
    • 同一のハッシュを持つランダムなデータをあっさり作れてしまう
    • パスワードハッシュとかには使わない
    • 短い文字列のmd5はGoogle先生に聞くと簡単に復元できる
    • [5d41402abc4b2a76b9719d911017c592] (https://www.google.co.jp/search?q=5d41402abc4b2a76b9719d911017c592)
  • 攻撃等が介在しない範囲では同一性チェックに最適なハッシュ関数

sha1

  • ビット長: 160
  • 16進表記の長さ: 40文字
  • Hash関数のデファクトスタンダード
  • gitの内部でも使われている
  • セキュリティとしてはやや脆弱
    • 直ちに危険ではないが、危ないという認識は持っておく
    • 証明書としての利用はsha2への移行が強く推奨されている

sha256:

  • ビット長: 256
  • 16進表記の長さ: 64文字
  • sha2の一種
  • 32ビットベースのCPUでの計算を想定して作られている
  • 正しく使えばセキュリティ性は十分高い

sha224

  • ビット長: 224
  • 16進表記の長さ: 56文字
  • sha256を計算して単純に切り詰めたもの
  • 切り落としてる分sha256に比べて弱い

sha512:

  • 16進表記の長さ: 128文字
  • sha2の一種
  • 64ビットベースのCPUでの計算を想定して作られている

crc32

  • 16進表記の長さ: 8文字
  • 32bitのハッシュが取れるのでIntegerと相性が良い
  • 文字列を均等に数値へ振り分けたいときに便利
  • 計算も早い
  • セキュリティ性は皆無
    • そもそも暗号でない
    • 間違っても意図的な改ざんチェックやパスワードハッシュに使ってはいけない
  • 生成方法が一意でないのでハッシュ実行のプログラムが異なると変わる可能性
  • データベースで検索キーが長い文字列の時(メアドとか)とかに、この関数でハッシュした補助インデックスを使うと効率が良くなる。

安全なパスワードハッシュ

セキュリティの重要課題:ユーザーのパスワードを安全に保管する方法について
安全なパスワードハッシュについてはこちらがとても参考になります。

sha2であればハッシュから元のデータを復元されるケースはかなり少ないです。
しかし、それは元のデータが十分長い場合に限られるので、
パスワードをハッシュして保存するような場合では適していません。

5文字程度のパスワードであればハッシュをGoogleで検索するだけで、
簡単に元の文字列が判明してしまいますし、
パスワードによく使われる文字列のデータベースを使えば、
総当りでもそんなに時間がかからず解析されてしまいます。

実際、2 万ドルを下回る最新のハッシュクラッキングサーバー は、100,000,000,000 以上の SHA-256 ハッシュを毎秒計算できます。
これだけの早さでsha256ハッシュが生成できてしまうそうです。
パスワード自身の文字数が少なければあっという間に復元できてしまいますね。

このクラックを防ぐためにはハッシュのアルゴリズムを複雑にして
1つのハッシュの計算時間を長くするしかありません。
1つのハッシュを計算するのにかかる時間を10倍にすれば、
総当りによる解析の耐性も10倍増やすことが可能です。

PBKDF2

では実際にどんな対策をすればよいか、という一つの答えがこのPBKDF2です。
このアルゴリズムはsha256といったハッシュ関数を
何千、何万回と繰り返し適応させることで計算コストを増やし
解析への耐性をつけることが可能になります。

Pythonであれば
pipからライブラリを落として利用することができます。
https://www.dlitz.net/software/python-pbkdf2/
(DjangoやFlaskだと標準で付属してるみたいです。)

import binascii
import hashlib
from pbkdf2 import PBKDF2

def safe_password_hash(password):
    salt = binascii.b2a_hex(os.urandom(keylen))
    iterations=10000

    hashed = PBKDF2(password, salt, iterations, digestmodule=hashlib.sha256).read(32)
    return hashed

デフォルトだと繰り返し回数やHash関数が上の記事にある方法とは異なるので
オプションとしてより適切な値を渡してあげてます。