This PEP proposes the addition of a module for common security-relatedfunctions such as generating tokens to the Python standard library.
Some common abbreviations used in this proposal:
Pseudo Random Number Generator. A deterministic algorithm usedto produce random-looking numbers with certain desirablestatistical properties.
Cryptographically Strong Pseudo Random Number Generator. Analgorithm used to produce random-looking numbers which areresistant to prediction.
Mersenne Twister. An extensively studied PRNG which is currentlyused by therandom module as the default.
This proposal is motivated by concerns that Python’s standard librarymakes it too easy for developers to inadvertently make serious securityerrors. Theo de Raadt, the founder of OpenBSD, contacted Guido van Rossumand expressed some concern[1] about the use of MT for generating sensitiveinformation such as passwords, secure tokens, session keys and similar.
Although the documentation for therandom module explicitly states thatthe default is not suitable for security purposes[2], it is stronglybelieved that this warning may be missed, ignored or misunderstood bymany Python developers. In particular:
The first[3] hit when searching for “python how to generate passwords” onGoogle is a tutorial that uses the default functions from therandommodule[4]. Although it is not intended for use in web applications, it islikely that similar techniques find themselves used in that situation.The second hit is to a StackOverflow question about generatingpasswords[5]. Most of the answers given, including the accepted one, usethe default functions. When one user warned that the default could beeasily compromised, they were told “I think you worry too much.”[6]
This strongly suggests that the existingrandom module is an attractivenuisance when it comes to generating (for example) passwords or securetokens.
Additional motivation (of a more philosophical bent) can be found in thepost which first proposed this idea[7].
Alternative proposals have focused on the default PRNG in therandommodule, with the aim of providing “secure by default” cryptographicallystrong primitives that developers can build upon without thinking aboutsecurity. (See Alternatives below.) This proposes a different approach:
To do this, this PEP proposes that we add a new module to the standardlibrary, with the suggested namesecrets. This module will contain aset of ready-to-use functions for common activities with securityimplications, together with some lower-level primitives.
The suggestion is thatsecrets becomes the go-to module for dealingwith anything which should remain secret (passwords, tokens, etc.)while therandom module remains backward-compatible.
This PEP proposes the following functions for thesecrets module:
secrets.token_bytes;secrets.token_hex;secrets.token_urlsafe.os.urandomdirectly orrandom.SystemRandom. Unlike therandom module, thisdoes not need to provide methods for seeding, getting or setting thestate, or any non-uniform distributions. It should provide thefollowing:secrets.choice.secrets.randbits.secrets.randbelow[9].secrets.compare_digest.The consensus appears to be that there is no need to add a new CSPRNG totherandom module to support these uses,SystemRandom will besufficient.
Some illustrative implementations have been given by Alyssa (Nick) Coghlan[10]and a minimalist API by Tim Peters[11]. This idea has also been discussedon the issue tracker for the “cryptography” module[12]. The followingpseudo-code should be taken as the starting point for the realimplementation:
fromrandomimportSystemRandomfromhmacimportcompare_digest_sysrand=SystemRandom()randbits=_sysrand.getrandbitschoice=_sysrand.choicedefrandbelow(exclusive_upper_bound):return_sysrand._randbelow(exclusive_upper_bound)DEFAULT_ENTROPY=32# bytesdeftoken_bytes(nbytes=None):ifnbytesisNone:nbytes=DEFAULT_ENTROPYreturnos.urandom(nbytes)deftoken_hex(nbytes=None):returnbinascii.hexlify(token_bytes(nbytes)).decode('ascii')deftoken_urlsafe(nbytes=None):tok=token_bytes(nbytes)returnbase64.urlsafe_b64encode(tok).rstrip(b'=').decode('ascii')
Thesecrets module itself will be pure Python, and other Pythonimplementations can easily make use of it unchanged, or adapt it asnecessary. An implementation can be found on BitBucket[13].
One difficult question is “How many bytes should my token be?”. We canhelp with this question by providing a default amount of entropy for the“token_*” functions. If thenbytes argument is None or not given, thedefault entropy will be used. This default value should be large enoughto be expected to be secure for medium-security uses, but is expected tochange in the future, possibly even in a maintenance release[14].
One question is the naming conventions used in the module[15], whether touse C-like naming conventions such as “randrange” or more Pythonic namessuch as “random_range”.
Functions which are simply bound methods of the privateSystemRandominstance (e.g.randrange), or a thin wrapper around such, should keepthe familiar names. Those which are something new (such as the varioustoken_* functions) will use more Pythonic names.
One alternative is to change the default PRNG provided by therandommodule[16]. This received considerable scepticism and outright opposition:
random module is for simple “guess a number”games written by beginners, and many people are loath to make anychange to therandom module which may make that harder.random module,there was considerable hostility to the idea of having to opt-in toa non-CSPRNG or any backwards-incompatible changes.Alyssa Coghlan made anearlier suggestionfor a globally configurable PRNGwhich uses the system CSPRNG by default, but has since withdrawn itin favour of this proposal.
PHP includes a functionuniqid[18] which by default returns athirteen character string based on the current time in microseconds.Translated into Python syntax, it has the following signature:
defuniqid(prefix='',more_entropy=False)->str
The PHP documentation warns that this function is not suitable forsecurity purposes. Nevertheless, various mature, well-known PHPapplications use it for that purpose (citation needed).
PHP 5.3 and better also includes a functionopenssl_random_pseudo_bytes[19]. Translated into Python syntax, it has roughly the followingsignature:
defopenssl_random_pseudo_bytes(length:int)->Tuple[str,bool]
This function returns a pseudo-random string of bytes of the givenlength, and a boolean flag giving whether the string is consideredcryptographically strong. The PHP manual suggests that returninganything but True should be rare except for old or broken platforms.
Based on a rather cursory search[20], there do not appear to be anywell-known standard functions for producing strong random values inJavaScript.Math.random is often used, despite serious weaknessesmaking it unsuitable for cryptographic purposes[21]. In recent yearsthe majority of browsers have gained support forwindow.crypto.getRandomValues[22].
Node.js offers a rich cryptographic module,crypto[23], most ofwhich is beyond the scope of this PEP. It does include a single functionfor generating random bytes,crypto.randomBytes.
The Ruby standard library includes a moduleSecureRandom[24]which includes the following methods:
There was a proposal to add a “random.safe” submodule, quoting the Zenof Python “Namespaces are one honking great idea” koan. However, theauthor of the Zen, Tim Peters, has come out against this idea[25], andrecommends a top-level module.
In discussion on the python-ideas mailing list so far, the name “secrets”has received some approval, and no strong opposition.
There is already an existing third-party module with the same name[26],but it appears to be unused and abandoned.
A: The consensus among security professionals is that MT is not safein security contexts. It is not difficult to reconstruct the internalstate of MT[27][28] and so predict all past and future values. Thereare a number of known, practical attacks on systems using MT forrandomness[29].
A: Yes. There have been vulnerabilities in Zope and Plone at the veryleast. Hanno Schlichting commented[30]:
"In the context of Plone and Zope a practical attack wasdemonstrated,butIcan't find any good non-broken links aboutthisanymore.IIRCPlonegeneratedarandomnumberandexposedthisoneacherrorpagealongthelinesof'Sorry, you encounteredanerror,yourproblemhasbeenfiledas<randomnumber>,pleaseincludethiswhenyoucontactus'. This allowed anyone to do largenumbersofrequeststothispageandgetenoughrandomvaluestoreconstructtheMTstate.Acoupleofsecurityrelatedmodulesusedrandominsteadofsystemrandom(cookiesessionids,passwordresetlinks,authtoken),sotheattackercouldbreakallofthose."
Christian Heimes reported this issue to the Zope security team in 2012[31],there are at least two related CVE vulnerabilities[32], and at least onework-around for this issue in Django[33].
A: No. This is a “batteries included” solution, not a full-featured“nuclear reactor”. It is intended to mitigate against some basicsecurity errors, not be a solution to all security-related issues. Toquote Alyssa Coghlan referring to her earlier proposal[34]:
"...folks really are better off learning to use things likecryptography.ioforsecuritysensitivesoftware,sothischangeisjustaboutharmmitigationgiventhatit's inevitable that anon-trivialproportionofthemillionsofcurrentandfuturePythondeveloperswon't do that."
A: The consensus is that the requirements for password generators are toovariable for it to be a good match for the standard library[35]. No passwordgenerator will be included in the initial release of the module, instead itwill be given in the documentation as a recipe (à la the recipes in theitertools module)[36].
secrets use /dev/random (which blocks) or /dev/urandom (whichdoesn’t block) on Linux? What about other platforms?A:secrets will be based onos.urandom andrandom.SystemRandom,which are interfaces to your operating system’s best source of cryptographicrandomness. On Linux, that may be/dev/urandom[37], on Windows it may beCryptGenRandom(), but see the documentation and/or source code for thedetailed implementation details.
randbelow, and not similar functionsrandrange orrandint.http://code.activestate.com/lists/python-dev/138375/This document has been placed in the public domain.
Source:https://github.com/python/peps/blob/main/peps/pep-0506.rst
Last modified:2025-02-01 08:59:27 GMT