PEP 506 – Adding A Secrets Module To The Standard Library | peps.python.org (original) (raw)

Author:

Steven D’Aprano

Status:

Final

Type:

Standards Track

Created:

19-Sep-2015

Python-Version:

3.6

Post-History:


Table of Contents

Abstract

This PEP proposes the addition of a module for common security-related functions such as generating tokens to the Python standard library.

Definitions

Some common abbreviations used in this proposal:

Rationale

This proposal is motivated by concerns that Python’s standard library makes it too easy for developers to inadvertently make serious security errors. Theo de Raadt, the founder of OpenBSD, contacted Guido van Rossum and expressed some concern [1] about the use of MT for generating sensitive information such as passwords, secure tokens, session keys and similar.

Although the documentation for the random module explicitly states that the default is not suitable for security purposes [2], it is strongly believed that this warning may be missed, ignored or misunderstood by many Python developers. In particular:

The first [3] hit when searching for “python how to generate passwords” on Google is a tutorial that uses the default functions from the randommodule [4]. Although it is not intended for use in web applications, it is likely that similar techniques find themselves used in that situation. The second hit is to a StackOverflow question about generating passwords [5]. Most of the answers given, including the accepted one, use the default functions. When one user warned that the default could be easily compromised, they were told “I think you worry too much.” [6]

This strongly suggests that the existing random module is an attractive nuisance when it comes to generating (for example) passwords or secure tokens.

Additional motivation (of a more philosophical bent) can be found in the post which first proposed this idea [7].

Proposal

Alternative proposals have focused on the default PRNG in the randommodule, with the aim of providing “secure by default” cryptographically strong primitives that developers can build upon without thinking about security. (See Alternatives below.) This proposes a different approach:

To do this, this PEP proposes that we add a new module to the standard library, with the suggested name secrets. This module will contain a set of ready-to-use functions for common activities with security implications, together with some lower-level primitives.

The suggestion is that secrets becomes the go-to module for dealing with anything which should remain secret (passwords, tokens, etc.) while the random module remains backward-compatible.

API and Implementation

This PEP proposes the following functions for the secrets module:

The consensus appears to be that there is no need to add a new CSPRNG to the random module to support these uses, SystemRandom will be sufficient.

Some illustrative implementations have been given by Alyssa (Nick) Coghlan [10]and a minimalist API by Tim Peters [11]. This idea has also been discussed on the issue tracker for the “cryptography” module [12]. The following pseudo-code should be taken as the starting point for the real implementation:

from random import SystemRandom from hmac import compare_digest

_sysrand = SystemRandom()

randbits = _sysrand.getrandbits choice = _sysrand.choice

def randbelow(exclusive_upper_bound): return _sysrand._randbelow(exclusive_upper_bound)

DEFAULT_ENTROPY = 32 # bytes

def token_bytes(nbytes=None): if nbytes is None: nbytes = DEFAULT_ENTROPY return os.urandom(nbytes)

def token_hex(nbytes=None): return binascii.hexlify(token_bytes(nbytes)).decode('ascii')

def token_urlsafe(nbytes=None): tok = token_bytes(nbytes) return base64.urlsafe_b64encode(tok).rstrip(b'=').decode('ascii')

The secrets module itself will be pure Python, and other Python implementations can easily make use of it unchanged, or adapt it as necessary. An implementation can be found on BitBucket [13].

Default arguments

One difficult question is “How many bytes should my token be?”. We can help with this question by providing a default amount of entropy for the “token_*” functions. If the nbytes argument is None or not given, the default entropy will be used. This default value should be large enough to be expected to be secure for medium-security uses, but is expected to change in the future, possibly even in a maintenance release [14].

Naming conventions

One question is the naming conventions used in the module [15], whether to use C-like naming conventions such as “randrange” or more Pythonic names such as “random_range”.

Functions which are simply bound methods of the private SystemRandominstance (e.g. randrange), or a thin wrapper around such, should keep the familiar names. Those which are something new (such as the varioustoken_* functions) will use more Pythonic names.

Alternatives

One alternative is to change the default PRNG provided by the randommodule [16]. This received considerable scepticism and outright opposition:

Alyssa Coghlan made an earlier suggestionfor a globally configurable PRNG which uses the system CSPRNG by default, but has since withdrawn it in favour of this proposal.

Comparison To Other Languages

What Should Be The Name Of The Module?

There was a proposal to add a “random.safe” submodule, quoting the Zen of Python “Namespaces are one honking great idea” koan. However, the author of the Zen, Tim Peters, has come out against this idea [25], and recommends 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.

Frequently Asked Questions

References

This document has been placed in the public domain.