|
|
Log in / Subscribe / Register

Security

Python's os.urandom() in the absence of entropy

By Jonathan Corbet
July 10, 2016
Python applications, like those written in other languages, often need to obtain random data for purposes ranging from cryptographic key generation to initialization of scientific models. For years, the standard way of getting that data is via a call to os.urandom(), which is documented to "return a string of n random bytes suitable for cryptographic use." An enhancement in Python 3.5 caused a subtle change in how os.urandom() behaves on Linux systems, leading to some long, heated discussions about how randomness should be obtained in Python programs. When the dust settles, Python benevolent dictator for life (BDFL) Guido van Rossum will have the unenviable task of choosing between two competing proposals.

Blocking os.urandom()

Traditionally, os.urandom() has been implemented on Linux by opening /dev/urandom and reading the requested amount of data. This interface is non-blocking; it will not wait if the amount of entropy in the system's entropy pool is low. The implementation of /dev/urandom is such that the quality of the random data it returns will be high even if the entropy pool is depleted — with one possible exception. Immediately after the system boots, when the entropy pool will contain little or no entropy, /dev/urandom may return relatively predictable data. In most systems, this window of poor randomness is only open for a few seconds at most, but exceptions do exist.

In the Python 3.5.0 release, os.urandom() was changed to use the relatively new getrandom() system call on Linux. Unless it has been called with the GRND_NONBLOCK flag, getrandom() will wait, if need be, for the system entropy pool to be initialized. os.urandom() does not supply that flag, meaning that it can block if the entropy pool has not yet accumulated enough randomness. It seems like a relatively small change, with the prospect of being sure of living up to the "suitable for cryptographic use" promise in compensation. But one need only look at Python issue 26839 to see that the implications are not quite as simple as one would expect.

It turns out that, in some distribution configurations, Python scripts are run at the very beginning of the user-space bootstrap process. If the entropy pool is not yet ready, those scripts will block until entropy-pool initialization is complete. If there is nothing else going on, and especially if the system is booting as a virtualized guest, it may take a long time to accumulate enough entropy to proceed. In the incident that led to the bug report, the boot process simply hung for 90 seconds until systemd lost patience and killed the blocking process. That kind of behavior, created in the search for unpredictable random numbers, has quite predictable effects in the form of unhappy users.

From this sprung the bug-tracker entry referenced above, which turned into a fierce discussion on the wisdom of the API change and whether os.urandom() should return crypto-quality randomness at any cost. The discussion spilled over onto the python-dev list when 3.5 release manager Larry Hastings despaired of reaching any sort of consensus and asked Van Rossum to simply rule on the matter. The resulting thread led many participants to question whether they wanted to continue following the list at all but, in the end, it did come to some useful conclusions.

If one is doing cryptographically sensitive work early in the bootstrap process — generating an SSH host key, for example — then blocking the boot almost certainly makes sense. The consequences of the alternative — generating weak keys — can be severe. In this case, though, it turns out that such high-quality randomness was not needed. Nobody was generating keys; instead, Python was initializing its own internal random-number generator and setting up dictionary randomization to defend against hash-collision attacks. These internal calls were (inadvertently) changed when os.urandom() was changed, but there seems to be a rough consensus that they do not need blocking behavior.

So the proper fix for the observed boot hang is to do these internal initializations without blocking on the entropy pool. For Python 3.5, the os.urandom() change will also be partially reverted, in that the function will, once again, be non-blocking. It will call getrandom() with the GRND_NONBLOCK flag and, if that call fails, fall back to reading /dev/urandom as before. With these fixes in place, the blocking part of the change is effectively reverted and the immediate problem has been solved.

Blocking or exceptions?

That still leaves open the issue of how os.urandom() should behave; developers who are concerned about security are adamant that it should not return data when the entropy pool is not yet ready. So there is still pressure on Van Rossum (and the community as a whole) to specify blocking behavior starting with the upcoming 3.6 release. Python's benevolent dictator seems inclined to downplay the issue:

The problem with security experts is that they're always right when they say you shouldn't do something. The only truly secure computer is one that's disconnected and buried 6 feet under the ground. There's always a scenario through which an attacker could exploit a certain behavior. And there's always the possibility that the computer that's thus compromised is guarding a list of Chinese dissidents, or a million credit card numbers, or the key Apple uses to sign iPhone apps. But much more likely it just has my family photos and 100 cloned GitHub projects.

It became clear in the discussion, though, that opposition to returning questionable randomness from os.urandom() is strong. It seems likely that, in 3.6 and later releases, os.urandom() will no longer return data drawn from an uninitialized entropy pool. The question of how it will behave is, as yet, unresolved, though. In the end, Van Rossum asked the proponents of two different approaches to write up their ideas as Python enhancement proposals (PEPs); he will then choose between the two.

The first approach, favored by Victor Stinner, is to simply make os.urandom() blocking and be done with it — after ensuring that Python itself uses non-blocking behavior during its initialization. Changing to blocking behavior is arguably an incompatible change in a longstanding Python API but, as Stinner points out: "First of all, no user complained yet that 'os.urandom()' blocks. This point is currently theoretical." As long as the problems with starting Python itself are resolved, the thinking goes, there should not be problems for other users.

The alternative comes from Nick Coghlan. With this proposal, os.urandom() will raise a BlockingIOError exception if random data cannot be had without blocking. Adding a new exception to an established API has its own hazards; no existing code will be expecting that exception, so surprising explosions might result. But, for a problem that should only be possible during the bootstrap process, Coghlan believes that this is the best approach:

The hard part is then knowing that you *need* to wait. If you're silently getting more-predictable-than-you-expected random data, you may never realise. If your system hangs, you might eventually figure it out, but only after a likely frustrating debugging effort. By contrast, if your application fails with "BlockingIOError: system random number generator not ready", then you can search for that on the internet, see the above snippet for "How to wait for the system random number generator to be ready on Linux" and stick that into your code.

This proposal also envisions adding a function to the in-development "secrets" modulesecrets.wait_for_system_rng() — that would simply block until the system's entropy pool is fully initialized and ready. The small (possibly nonexistent) body of code that breaks with unhandled BlockingIOError exceptions could call this function to ensure the availability of strong random data from os.urandom().

It is not clear when a decision between these two proposals will be made. It is worth noting, though, that Coghlan has indicated that he is happy enough with Stinner's proposal that he can support it should that be the one that is accepted in the end. So the discussion may have been long and painful, but the end result should be strong random data in Python in a way that the community as a whole is able to agree upon. Hopefully that means everybody can rest and prepare for the inevitable debate over whether this change should be backported to Python 2.

Comments (26 posted)

Brief items

Security quotes of the week

While all three arms of the US government have drawn back on surveillance powers following the Snowden revelations, Theresa May has taken the hardest possible line. Her Investigatory Powers Bill will give her successors as Home Secretary sweeping powers to order firms in the UK to hand over data and help GCHQ hack their customers. Brexit will shield these powers from challenge in the European Court of Justice, making it much harder for a UK company to claim “adequacy” for its data protection arrangements in respect of EU data subjects. This will make it still less attractive for an IT company to keep in the UK either data that could be seized or engineering staff who could be coerced.
Ross Anderson

The researchers used "honeypot" .onion servers to find the spying computers: these honeypots were .onion sites that the researchers set up in their own lab and then connected to repeatedly over the Tor network, thus seeding many Tor nodes with the information of the honions' existence. They didn't advertise the honions' existence in any other way and there was nothing of interest at these sites, and so when the sites logged new connections, the researchers could infer that they were being contacted by a system that had spied on one of their Tor network circuits.

This attack was already understood as a theoretical problem for the Tor project, which had recently undertaken a rearchitecting of the hidden service system that would prevent it from taking place.

No one knows who is running the spying nodes: they could be run by criminals, governments, private suppliers of "infowar" weapons to governments, independent researchers, or other scholars (though scholarly research would not normally include attempts to hack the servers once they were discovered).

Cory Doctorow comments on an upcoming DEF CON presentation

It's certainly interesting that Google is thinking about this [post-quantum cryptography], and probably okay that it's available in the Canary version of Chrome, but this algorithm is by no means ready for operational use. Secure public-key algorithms are very hard to create, and this one has not had nearly enough analysis to be trusted. Lattice-based public-key cryptosystems such as New Hope are particularly subtle -- and we cryptographers are still learning a lot about how they can be broken.

Targets are important in cryptography, and Google has turned New Hope into a good one. Consider this an opportunity to advance our cryptographic knowledge, not an offer of a more-secure encryption option. And this is the right time for this area of research, before quantum computers make discrete-logarithm and factoring algorithms obsolete.

Bruce Schneier

Comments (none posted)

New vulnerabilities

community-mysql: unspecified

Package(s):community-mysql CVE #(s):
Created:July 11, 2016 Updated:July 13, 2016
Description: Latest upstream release, 5.7.12, fixes unspecified vulnerabilities.
Alerts:
Fedora FEDORA-2016-dfa325d31b community-mysql 2016-07-10

Comments (none posted)

davfs2: unspecified

Package(s):davfs2 CVE #(s):
Created:July 11, 2016 Updated:July 13, 2016
Description: Update to the latest upstream release to fix unspecified vulnerabilities. See the Red Hat bugzilla for more information.
Alerts:
Fedora FEDORA-2016-5e7abcde9d davfs2 2016-07-10

Comments (none posted)

gnutls: certificate verification vulnerability

Package(s):gnutls CVE #(s):
Created:July 12, 2016 Updated:July 25, 2016
Description: From the Red Hat bugzilla:

A vulnerability was discovered in gnutls that affects certificate verification when GnuTLS is used in combination with the p11-kit trust module. This issue affects gnutls 3.3.23, 3.4.12 and later versions.

Alerts:
Fedora FEDORA-2016-4738cb1a2c mingw-gnutls 2016-07-22
Fedora FEDORA-2016-446eaaf618 gnutls 2016-07-14
Fedora FEDORA-2016-2a5046f726 gnutls 2016-07-12

Comments (none posted)

gsi-openssh: support GSI authentication

Package(s):gsi-openssh CVE #(s):
Created:July 12, 2016 Updated:July 13, 2016
Description: From the Fedora advisory:

This version of OpenSSH has been modified to support GSI authentication.

This package includes the core files necessary for both the gsissh client and server. To make this package useful, you should also install gsi-openssh-clients, gsi-openssh-server, or both.

Alerts:
Fedora FEDORA-2016-3f128cf0ce gsi-openssh 2016-07-12

Comments (none posted)

httpd: authentication bypass

Package(s):httpd CVE #(s):CVE-2016-4979
Created:July 12, 2016 Updated:July 18, 2016
Description: From the CVE entry:

The Apache HTTP Server 2.4.18 through 2.4.20, when mod_http2 and mod_ssl are enabled, does not properly recognize the "SSLVerifyClient require" directive for HTTP/2 request authorization, which allows remote attackers to bypass intended access restrictions by leveraging the ability to send multiple requests over a single connection and aborting a renegotiation.

Alerts:
Gentoo 201610-02 apache 2016-10-06
Red Hat RHSA-2016:1420-01 httpd24-httpd 2016-07-18
Fedora FEDORA-2016-e256a03791 httpd 2016-07-15
Fedora FEDORA-2016-c7288a5b36 httpd 2016-07-12

Comments (none posted)

libgd2: denial of service

Package(s):libgd2 CVE #(s):CVE-2016-6161
Created:July 12, 2016 Updated:July 27, 2016
Description: From the Ubuntu advisory:

It was discovered that the GD library incorrectly handled memory when encoding a GIF image. A remote attacker could possibly use this issue to cause a denial of service.

Alerts:
SUSE SUSE-SU-2016:2460-2 php7 2016-11-01
SUSE SUSE-SU-2016:2460-1 php7 2016-10-05
openSUSE openSUSE-SU-2016:2451-1 php5 2016-10-04
Fedora FEDORA-2016-0de0e0ee0c gd 2016-10-05
SUSE SUSE-SU-2016:2408-1 php5 2016-09-28
openSUSE openSUSE-SU-2016:2363-1 gd 2016-09-24
openSUSE openSUSE-SU-2016:2117-1 gd 2016-08-19
openSUSE openSUSE-SU-2016:2071-1 php5 2016-08-15
Debian-LTS DLA-563-1 libgd2 2016-07-26
Debian DSA-3619-1 libgd2 2016-07-15
Ubuntu USN-3030-1 libgd2 2016-07-11

Comments (none posted)

nodejs-ws: denial of service

Package(s):nodejs-ws CVE #(s):
Created:July 11, 2016 Updated:July 13, 2016
Description: From the Red Hat bugzilla:

ws is a "simple to use, blazing fast and thoroughly tested websocket client, server and console for node.js, up-to-date against RFC-6455"

By sending an overly long websocket payload to a ws server, it is possible to crash the node process.

Alerts:
Fedora FEDORA-2016-d97547150a nodejs-ws 2016-07-09
Fedora FEDORA-2016-40bbb1efe6 nodejs-ws 2016-07-10

Comments (none posted)

php5: cross-site scripting

Package(s):php5 CVE #(s):CVE-2015-8935
Created:July 8, 2016 Updated:July 13, 2016
Description: From the openSUSE advisory:

CVE-2015-8935: XSS in header() with Internet Explorer (bsc#986004)

Alerts:
SUSE SUSE-SU-2016:2080-1 php5 2016-08-16
SUSE SUSE-SU-2016:2013-1 php53 2016-08-09
Ubuntu USN-3045-1 php5, php7.0 2016-08-02
openSUSE openSUSE-SU-2016:1922-1 php5 2016-08-01
openSUSE openSUSE-SU-2016:1761-1 php5 2016-07-07

Comments (none posted)

samba: crypto downgrade

Package(s):samba CVE #(s):CVE-2016-2119
Created:July 8, 2016 Updated:December 19, 2016
Description: From the Slackware advisory:

This release fixes a security issue: Client side SMB2/3 required signing can be downgraded. It's possible for an attacker to downgrade the required signing for an SMB2/3 client connection, by injecting the SMB2_SESSION_FLAG_IS_GUEST or SMB2_SESSION_FLAG_IS_NULL flags. This means that the attacker can impersonate a server being connected to by Samba, and return malicious results.

Alerts:
Ubuntu USN-3092-1 samba 2016-09-28
openSUSE openSUSE-SU-2016:2371-1 samba 2016-09-24
Scientific Linux SLSA-2016:1487-1 samba4 2016-07-26
Scientific Linux SLSA-2016:1486-1 samba 2016-07-26
CentOS CESA-2016:1487 samba4 2016-07-26
CentOS CESA-2016:1486 samba 2016-07-26
Oracle ELSA-2016-1487 samba4 2016-07-26
Oracle ELSA-2016-1486 samba 2016-07-26
Red Hat RHSA-2016:1487-01 samba4 2016-07-26
Red Hat RHSA-2016:1486-01 samba 2016-07-26
openSUSE openSUSE-SU-2016:1830-1 samba 2016-07-19
Fedora FEDORA-2016-48b53757a9 samba 2016-07-15
Fedora FEDORA-2016-0acec022f4 samba 2016-07-12
Slackware SSA:2016-189-01 samba 2016-07-07
Debian DSA-3740-1 samba 2016-12-19

Comments (none posted)

tcpreplay: denial of service

Package(s):tcpreplay CVE #(s):CVE-2016-6160
Created:July 8, 2016 Updated:December 6, 2016
Description: From the Debian-LTS advisory:

The tcprewrite program, part of the tcpreplay suite, does not check the size of the frames it processes. Huge frames may trigger a segmentation fault, and such frames occur when caputuring packets on interfaces with an MTU of or close to 65536. For example, the loopback interface lo of the Linux kernel has such a value.

Alerts:
Fedora FEDORA-2016-72dae8ea7e tcpreplay 2016-07-18
Fedora FEDORA-2016-904ed1d231 tcpreplay 2016-07-18
Mageia MGASA-2016-0247 tcpreplay 2016-07-08
Debian-LTS DLA-544-1 tcpreplay 2016-07-07
openSUSE openSUSE-SU-2016:3013-1 tcpreplay 2016-12-05

Comments (none posted)

Page editor: Jake Edge
Next page: Kernel development>>


Copyright © 2016, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds