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

バージョン 3.6 で追加.

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

======================================================================

"secrets" モジュールを使って、パスワードやアカウント認証、セキュリティ
トークンなどの機密を扱うのに適した、暗号学的に強い乱数を生成することが
できます。

特に、 "random" モジュールのデフォルトの擬似乱数ジェネレータよりも
"secrets" を使用するべきです。 "random" モジュールはモデル化やシミュレ
ーション向けで、セキュリティや暗号学的に設計されてはいません。

参考: **PEP 506**


乱数
====

"secrets" モジュールは OS が提供する最も安全な乱雑性のソースへのアクセ
スを提供します。

class secrets.SystemRandom

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

secrets.choice(sequence)

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

secrets.randbelow(n)

   [0, *n*) のランダムな整数を返します。

secrets.randbits(k)

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


トークンの生成
==============

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

secrets.token_bytes([nbytes=None])

   *nbytes* バイトを含むバイト文字列を返します。*nbytes* が "None" の
   場合や与えられなかった場合は妥当なデフォルト値が使われます。

      >>> token_bytes(16)  
      b'\xebr\x17D*t\xae\xd4\xe3S\xb6\xe2\xebP1\x8b'

secrets.token_hex([nbytes=None])

   十六進数のランダムなテキスト文字列を返します。文字列は *nbytes* の
   ランダムなバイトを持ち、各バイトは二つの十六進数に変換されます。
   *nbytes* が "None" の場合や与えられなかった場合は妥当なデフォルト値
   が使われます。

      >>> token_hex(16)  
      'f9bf78b9a18ce6d46a0cd2b0b86df9da'

secrets.token_urlsafe([nbytes=None])

   *nbytes* のランダムなバイトを持つ URL 安全なテキスト文字列を返しま
   す。テキストは Base64 でエンコードされていて、平均的に各バイトは約
   1.3 文字になります。  *nbytes* が "None" の場合や与えられなかった場
   合は妥当なデフォルト値が使われます。

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


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

総当たり攻撃 に耐えるには、トークンは十分にランダムでなければなりませ
ん。残念なことに、コンピュータの性能が向上し、より短時間により多くの推
測ができるようになるにつれ、十分とされるランダムさというのは必然的に増
えます。2015 年の時点で、"secrets" モジュールに想定される通常の用途で
は、32 バイト (256 ビット) のランダムさは十分と考えられています。

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

それ以外の場合、すなわち引数がない場合や "None" の場合、"token_*" 関数
は妥当なデフォルト値を代わりに使います。

注釈:

  デフォルトはメンテナンスリリースの間を含め、いつでも変更される可能性
  があります。


その他の関数
============

secrets.compare_digest(a, b)

   文字列 *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つと数字を少なくとも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
   # On standard Linux systems, use a convenient dictionary file.
   # Other platforms may need to provide their own word-list.
   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()
