secrets --- 機密を扱うために安全な乱数を生成する

Added in version 3.6.

ソースコード: Lib/secrets.py


secrets モジュールは、パスワード、アカウント認証、セキュリティトークン、その他の機密情報の管理に適した、暗号学的に強い乱数を生成するために使います。

特に、random モジュールのデフォルトの擬似乱数生成器よりも、secrets を優先して使ってください。 その擬似乱数生成器は、セキュリティや暗号処理ではなく、モデリングとシミュレーションのために設計されています。

参考

PEP 506

乱数

secrets モジュールは、オペレーティングシステムが提供する最も安全な乱数源へのアクセスを提供します。

class secrets.SystemRandom

OS が提供する最も高品質なソースを用いて乱数を生成するためのクラスです。更に詳しいことについては random.SystemRandom を参照してください。

secrets.choice(seq)

空でないシーケンスから要素をランダムに選択して返します。

secrets.randbelow(exclusive_upper_bound)

[0, exclusive_upper_bound) の範囲のランダムな整数を返します。

secrets.randbits(k)

ランダムな k ビットの非負の整数を返します。

トークンの生成

secrets モジュールは、パスワードのリセットや推測しにくい URL などの用途に適した、安全なトークンを生成する関数を提供します。

secrets.token_bytes(nbytes=None)

nbytes バイトのランダムなバイト文字列を返します。

nbytes を指定しない場合や None の場合は、代わりに DEFAULT_ENTROPY を使います。

>>> token_bytes(16)
b'\xebr\x17D*t\xae\xd4\xe3S\xb6\xe2\xebP1\x8b'
secrets.token_hex(nbytes=None)

16 進数で表したランダムな文字列を返します。 nbytes バイトのランダムなデータの各バイトを、16 進数の 2 桁に変換した文字列です。

nbytes を指定しない場合や None の場合は、代わりに DEFAULT_ENTROPY を使います。

>>> token_hex(16)
'f9bf78b9a18ce6d46a0cd2b0b86df9da'
secrets.token_urlsafe(nbytes=None)

nbytes バイトのランダムなデータを含む、URL に安全に使えるランダムな文字列を返します。 Base64 でエンコードするため、各バイトは平均して約 1.3 文字になります。

nbytes を指定しない場合や None の場合は、代わりに DEFAULT_ENTROPY を使います。

>>> token_urlsafe(16)
'Drmhze6EPcv0fN_81Bj-nA'

トークンは何バイト使うべきか?

総当たり攻撃 に対して安全であるには、トークンに十分なランダムさが必要です。 残念ながら、コンピューターの性能が向上し、短時間でより多くの候補を試せるようになるにつれて、十分とされるランダムさは増えていきます。 2015 年時点では、secrets モジュールに想定される典型的な用途には、32 バイト (256 ビット) のランダムさで十分と考えられています。

独自の長さのトークンを扱いたい場合、様々な token_* 関数に int 引数で渡すことで、トークンに使用するランダムさを明示的に指定することができます。引数はランダムさのバイト数として使用されます。

引数を指定しない場合や引数が None の場合、token_* 関数は代わりに DEFAULT_ENTROPY を使います。

secrets.DEFAULT_ENTROPY

token_* 関数が使う、ランダムなデータのデフォルトのバイト数。

具体的な値は、メンテナンスリリースも含め、いつでも変更される可能性があります。

その他の関数

secrets.compare_digest(a, b)

文字列または bytes-like object である a と b が等しければ True、それ以外は False を返します。 タイミング攻撃 のリスクを減らすため、「定数時間の比較」を使います。 詳しくは hmac.compare_digest() を参照してください。

レシピとベストプラクティス

この節では、secrets を使って基本的なセキュリティ水準を維持するためのレシピとベストプラクティスを示します。

8文字のアルファベットと数字を含むパスワードを生成するには:

import string
import secrets
alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for i in range(8))

注釈

アプリケーションは、平文であろうと暗号化されていようと、 復元可能な形式でパスワードを保存 してはいけません。パスワードは暗号学的に強い一方向 (非可逆) ハッシュ関数を用いてソルトしハッシュしなければなりません。

小文字を少なくとも1つ、大文字を少なくとも1つ、数字を少なくとも3つ含む、英数字10文字のパスワードを生成する例です。

import string
import secrets
alphabet = string.ascii_letters + string.digits
while True:
    password = ''.join(secrets.choice(alphabet) for i in range(10))
    if (any(c.islower() for c in password)
            and any(c.isupper() for c in password)
            and sum(c.isdigit() for c in password) >= 3):
        break

XKCD スタイルのパスフレーズ を生成するには:

import secrets
# 標準的な Linux システムでは、利用できる辞書ファイルを使う。
# 他のプラットフォームでは、単語リストを自分で用意する必要がある場合がある。
with open('/usr/share/dict/words') as f:
    words = [word.strip() for word in f]
    password = ' '.join(secrets.choice(words) for i in range(4))

パスワードの復元用途に適したセキュリティトークンを含む、推測しにくい一時 URL を生成するには:

import secrets
url = 'https://example.com/reset=' + secrets.token_urlsafe()