メモ_saltとStretchingで処置したパスワードに対するHash処理の速度

パスワードなどの重要な情報をHash化して扱うのは、オープン系システム構築時の基本の一つですが、最近推奨されているハッシュ化として、saltとstretchingという方法があります。

salt は ハッシュ化する文字列に、さらに別の文字列を加えて処理することで解読耐性を高める手法です。

stretching はハッシュ化を繰り返し行うことで、解読耐性を高める手法です。

 

実際、saltとstretchingは、どの程度の処理負荷でしょうか。手元で試してみました。

環境

CPU : Ryzen7 5825U

MEM : 16GB

OS: Windwos11 Home

VM : Virtual Box Version 7.0.6 / Ubuntu 22 LTS

 

手順1

まずはデータを用意します。英数字大文字小文字混在8桁のパスワードを1000個を作成します。以下のコードで1000個のパスワードを含むテキストファイルを作成します。


import random
import string

def random_string(length):
    letters = string.ascii_letters + string.digits
    return ''.join(random.choice(letters) for i in range(length))

with open('password.txt', 'w') as f:
    for i in range(1000):
        f.write(random_string(8) + '\n')
手順2

1000個のパスワードに対して、実際にハッシュ化してみます。sha256、saltはパスワード毎ユニーク20バイト、stretchingは10000回とします。


import hashlib
import os
import time

start_time = time.time()


iter_num = 10000

def hash_password(password):
    salt = os.urandom(20)
    password_hash = hashlib.sha256(password.encode() + salt).hexdigest()
    for i in range(iter_num):
        password_hash = hashlib.sha256(password_hash.encode() + salt).hexdigest()
    return password_hash, salt.hex()

with open('password.txt', 'r') as f:
    passwords = f.readlines()

with open('hashed_passwords.txt', 'w') as f:
    for password in passwords:
        password = password.strip()
        hashed_password, salt = hash_password(password)
        f.write(f'Password: {password}, Salt: {salt}, Hashed Password: {hashed_password}\n')




# 経過時間の表示
elapsed_time = time.time() - start_time
print(f'elapsed_time:{elapsed_time:.3f} sec')

# 毎時でのhash数
per_hour = ((60 * 60) / (elapsed_time / 1000)) 
print(f'per_hour:{per_hour}')
結果

処理結果はPythonで 10.408秒 でした。1000個で割れば、パスワード一つ当たりの処理は0.01秒と瞬間です。1時間あたりの処理数も推定で計算してますが、約327429 でしたので、1時間に最大で数万回くらいパスワード処理(新規登録時とかログイン時とか)されるシステムなら、普通のPCサーバで耐えられそうです。

ちなみに、sha512で試したところ 13.332 sec、sha3-512で 17.715 sec でした。もし重ければStretching回数を減らして、そのかわりsha512とかsha3-512にして調整とかが現実的ですかね。

 

解読耐性を高めることが推奨される背景

解読耐性を高めることが推奨される背景として、元のパスワードが8桁の英数字とすると、総当たり回数は約218兆(218340105584896)となり、最近のパワフルなコンピューターだとハッシュ値が解析される恐れがあります。

事前に総当たりでハッシュ値を計算・保存しておいて逆引きでパスワードを当てるレインボーテーブル・システムを組むとして、仮に8桁パスワードを総当たり(218兆個)で計算し結果を保存するには14ペタバイトになります(元パスワードとハッシュを合わせてchar64で入れるとした単純計算)。

これくらいだと、仮想通貨マイニング業者や、最大手のソシャゲ会社がログ管理とかで扱っている規模ですね。もちろんパスワードを10桁とか16桁とかのケースも考慮すると、さらにデータサイズは大きくなりますが、それでも数十ペタバイトくらいだと、現実的に扱えるビックデータに収まってしまうのが恐ろしいです(コストは最低でも数千万円から数億円のオーダーではありますが)

総当たり攻撃 - Wikipedia

レインボーテーブル - Wikipedia

ペタバイトとは?ギガバイト・テラバイトの違いや管理の方法など解説! (cloud-for-all.com)