"secrets" --- 產生用於管理機密的安全亂數
****************************************

3.6 版新加入.

**原始碼：**Lib/secrets.py

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

"secrets" 模組可用於產生高加密強度的亂數，適合用來管理諸如密碼、帳號認
證、安全性權杖（security tokens）這類資料，以及管理其他相關的機密資料
。

尤其應優先使用 "secrets" 作為預設來替代 "random" 模組中的預設偽亂數產
生器（pseudo-random number generator），該模組被設計用於建模和模擬，而
非用於安全性和加密。

也參考: **PEP 506**


亂數
====

"secrets" 模組使你得以存取作業系統所提供安全性最高的亂數產生器。

class secrets.SystemRandom

   一個用來產生亂數的類別，用的是作業系統提供的最高品質來源。 請參閱
   "random.SystemRandom" 以獲取更多細節。

secrets.choice(sequence)

   從一非空序列中，回傳一個隨機選取的元素。

secrets.randbelow(n)

   回傳一個 [0, *n*) 範圍之內的隨機整數。

secrets.randbits(k)

   回傳一個具 *k* 個隨機位元的整數。


產生權杖（token）
=================

"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])

   回傳一個 URL 安全的隨機文本字串，包含 *nbytes* 個隨機位元組。 文本
   將使用 Base64 編碼，因此平均來說每個位元組會對應到約 1.3 個字元。
   如果 *nbytes* 為 "None" 或未提供，則會使用一個合理的預設值。

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


權杖應當使用多少個位元組？
--------------------------

為了在面對暴力攻擊時能保證安全，權杖必須具有足夠的隨機性。 不幸的是，
對隨機性是否足夠的標準，會隨著電腦越來越強大並能夠在更短時間內進行更多
猜測而不斷提高。 在 2015 年時，人們認為 32 位元組（256 位元）的隨機性
對於 "secrets" 模組所預期的一般使用場景來說是足夠的。

對於想自行管理權杖長度的使用者，你可以對各種 "token_*" 函式明白地指定
"int" 引數（argument）來指定權杖要使用的隨機性程度。 該引數以位元組數
來表示要使用的隨機性程度。

否則，如未提供引數，或者如果引數為 "None"，則 "token_*" 函式則會使用一
個合理的預設值。

備註:

  該預設值可能在任何時候被改變，包括在維護版本更新的時候。


其他函式
========

secrets.compare_digest(a, b)

   如果字符串或 *字节型对象* *a* 与 *b* 相等则返回 "True"，否则返回
   "False"，使用了“常数时间比较”来降低 定时攻击 的风险。请参阅
   "hmac.compare_digest()" 了解更多细节。


應用技巧和典範實務（best practices）
====================================

本節展示了一些使用 "secrets" 來管理基本安全等級的應用技巧和典範實務。

產生八個字元長的字母數字密碼：

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

備註:

  應用程式不能以可復原的格式存儲密碼，無論是用純文本還是經過加密。 它
  們應當先加鹽（salt），再使用高加密強度的單向（不可逆）雜湊函數來產生
  雜湊值。

產生十個字元長的字母數字密碼，其中包含至少一個小寫字母，至少一個大寫字
母以及至少三個數字:

   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 風格的 passphrase:

   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()
