Python独習!

習得したPython知識をペイフォワード

Pythonでsecretsモジュールを使ってパスワードを自動生成する

Pythonでパスワード作るにはsecrets.choice()を使った方が良いと言われているけど、random.choice()と何が違うのか?
random.choice()で作成されたパスワードは予測される可能性がある。パスワードを構成するすべての文字がランダムに選ばれたように見えて実はどんな文字が選ばれているのかを第三者が計算することができる。 対して、secrets.choice()はより予測が難しくなったもの。

注意事項

secretsモジュールはより安全であると紹介しているサイトを見かけますが、PEP506のQ/Aに「専用の暗号化ソフトウェアの代用にはならない」との記述があります。過度な信頼をしてはいけないと私は解釈しました。

この記事はrandom.choice()よりはsecrets.choice()のほうがベターだよね、ということをお伝えするのが目的であり、安全であるとお伝えする意図は一切ありません。ご使用は自己責任でお願いします。

解説

random.choice()

random.Randomクラスの関数であり、Mersenne Twisterという乱数発生器(アルゴリズム)を使用しているが、暗号化の目的には向いていない。
実は、randomモジュールは別に random.SystemRandomクラス も提供していて、このクラスはOSがもつ乱数発生器を利用して乱数を作成する。

secrets.choice()

secrets.SystemRandomクラスの関数であり、このクラスもOSがもつ乱数発生器を利用して乱数を作成する。

結局は…

random.SystemRandomのインスタンスを作ってchoice()メソッドを使えば、OSがもつ乱数発生器を利用することができるのに、このことを知らないユーザーが多いので新たにsecretsモジュールを設けて間違いを防ぐことにしたらしい。というようなことがPEP506に書いてあった。

サンプルプログラム

アルファベットの大文字と小文字、数字0~9の中から10文字をランダムに選びパスワードを作成する。そして、パスワードをクリップボードにコピーし、念のためテキストファイル(.txt)にしてデスクトップにも保存する。というプログラム。
python_create_password_secrets

ソースコード

import os
import datetime
import string
import secrets
import ctypes
import pyperclip

#Determine password length
len_pw = 10

#Create password
def create_pw(length):
    chars = string.ascii_uppercase + string.ascii_lowercase + string.digits
    pw = ''.join([secrets.choice(chars) for i in range(length)])

    return pw

#Save as txt
def save_txt(password):
    now = datetime.datetime.now()
    filename = "PassWord_{:%Y%m%d%H%M%S}.txt".format(now)
    desktop = os.getenv("HOMEDRIVE") + os.getenv("HOMEPATH") + "\\Desktop"

    with open(os.path.join(desktop, filename), mode='a') as f:
        f.write(password)

def main():
    x = create_pw(len_pw)
    save_txt(x)
    pyperclip.copy(x)
    ctypes.windll.user32.MessageBoxW(
        0,
        "パスワードをクリップボードにコピーしました",
        "Complete!!",
        0x00000040)

if __name__ == '__main__':
    main()
/* -----codeの行番号----- */