nodejs / node

Node.js JavaScript runtime ✨🐢🚀✨
https://nodejs.org
Other
105.15k stars 28.49k forks source link

Use system random generator in crypto #5798

Closed speakeasypuncture closed 6 years ago

speakeasypuncture commented 8 years ago

randomBytes uses OpenSSL as its random number generator. It would be wiser and less errorprone to use a system RNG like urandom on Unix platforms, getrandom syscall on Linux and CryptGenRandom on Windows.

bnoordhuis commented 8 years ago

Instead of linking to a blog post, please summarize the pros and cons here.

ChALkeR commented 8 years ago

Perhaps @joepie91 or @paragonie-scott would be interested to elaborate that here.

paragonie-scott commented 8 years ago

From "a blog post":

Why not {SecureRandom, OpenSSL, havaged, &c}?

These are userspace CSPRNGs. You want to use the kernel’s CSPRNG, because:

  • The kernel has access to raw device entropy.
  • It can promise not to share the same state between applications.
  • A good kernel CSPRNG, like FreeBSD’s, can also promise not to feed you random data before it’s seeded.

Additionally, OpenSSL's userspace PRNG has caused issues in other languages (PHP under Apache for sure) because it's not fork-safe, which caused random numbers to repeat. (Combine this with the consequences of nonce reuse for most stream ciphers and boom your cryptosystem loses all privacy.)

A safer bet would be to adopt what PHP 7's random_bytes() does: https://github.com/php/php-src/blob/e8f056e535813bf864743626c3a208ceafee70b9/ext/standard/random.c#L83-L186

bnoordhuis commented 8 years ago

OpenSSL's PRNG is seeded from /dev/{,s,u}random, can get entropy from entropy-collecting daemons, and forking is not an issue for node.js. So far I'm not seeing a compelling reason to switch.

ChALkeR commented 8 years ago

@paragonie-scott That's does not look like a good reasoning. Could you please be more calm? ;-)

joepie91 commented 8 years ago

@bnoordhuis Trying to find specific reasons to explain away potential issues is not a good approach to security-related matters. The issues exist, and there's no way to predict how Node will evolve - for example, if fork-safety might start to matter due to future changes, at which point people will likely have forgotten about this specific issue.

We should be striving for the optimally secure implementation (within technical constraints), instead of attempting to 'defend' the current implementation when there are known edge cases / issues with it.

This post addresses that further.

ChALkeR commented 8 years ago

I do not yet have a strong opinion on this. I called @joepie91 and @paragonie-scott here because they expressed similar considerations as @speakeasypuncture in an IRC chat earlier.

@bnoordhuis, as I understand their points, the reasons here are:

  1. System PRNG is good for this, is known to be stable and hasn't caused many security issues.
  2. Adding a user-space PRNG of top of that doesn't make it more secure, but it makes it more prone to errors.
  3. OpenSSL isn't actually a very high code quality product.
  4. Using OpenSSL PRNG caused serious security issues for some projects, namely for Debian, Android and PHP (that latter is still not fixed, btw), though that was mostly their fault. Those issues are not directly applicable to Node.js, but this gives an uneasy feeling.
  5. There seems to be no reason not to ditch OpenSSL PRNG and reduce the possible errors.

Everyone — did I miss anything?

ChALkeR commented 8 years ago

Again: I do not yet have a strong opinion on this.

I would like to hear what would the drawbacks of this change be, and is there something where (or why) OpenSSL PRNG could be better than the system PRNG. Excluding the fact that it's already used, of course — that also isn't a good enough argument, this won't even be a semver-major change. It's not documented that crypto.randomBytes() uses OpenSSL.

ChALkeR commented 8 years ago

/cc @nodejs/crypto.

ChALkeR commented 8 years ago

@mscdex I'm not sure if this is a «feature request», it looks like a proposal to change the implementation of crypto.randomBytes() to me.

joepie91 commented 8 years ago

@ChALkeR Your summary seems accurate to me.

paragonie-scott commented 8 years ago

Also, random number generators are hot-swappable without compatibility concerns, only security concerns.

indutny commented 8 years ago

I'm -1 on this, no compelling reasons for me.

joepie91 commented 8 years ago

@indutny Please refer back to this comment in particular, plus several others in the thread. There is a lack of reasons not to do this (insofar anybody has stated them, that is), while there are documented reasons to do it.

If you feel that there is a reason not to do it, then please share it - but "no compelling reasons for me" really isn't a sufficient argument for a security-related matter. Even a small defect can have disastrous consequences.

indutny commented 8 years ago

@joepie91 sure, sorry for too short reply.

What about systems with "good" PRNG? How many of them are there? Do we have to carry both implementations to support them?

joepie91 commented 8 years ago

@indutny Thanks for the elaboration.

As I understand it (and please correct me if not), OpenSSL depends on the system PRNG to begin with. I'm not aware of any platforms (of those supported by Node, that is) where OpenSSL can provide a better PRNG than the one that the system offers natively.

It should thus be possible to just remove OpenSSL's PRNG from the equation entirely, and rely purely on the system PRNG, as PHP has done.

indutny commented 8 years ago

@joepie91 do you suggest to use this randomness for TLS protocol as well? I'm not sure if it is possible, though.

joepie91 commented 8 years ago

While that would probably be nice (albeit requiring more investigation), I don't think that's doable. As far as I know, OpenSSL's other functionality relies internally on its own PRNG with no ability to change that - unless we want to get rid of OpenSSL entirely, which would be a separate proposal (and likely not viable at this stage, given the lack of well-tested alternatives).

So, this specific proposal would concern the "user-facing" randomBytes method only.

indutny commented 8 years ago

Ok, considering arguments it probably make more sense now.

bnoordhuis commented 8 years ago

Here's a reason not to switch: OpenSSL's PRNG is a known quantity, the strength of platform-specific PRNGs is not. If the next Windows or FreeBSD release has a flawed PRNG, that will compromise node.js.

If (generic) you think moving to platform-specific solutions is the way forward, get OpenSSL to adopt your approach and node.js will automatically go along. I believe @paragonie-scott is volunteering? He sure seems to feel strongly about it.

if fork-safety might start to matter due to future changes, at which point people will likely have forgotten about this specific issue.

@joepie91 The bucket list of fork-safety issues that would have to be addressed is so long that I think it's safe to say that node.js will never be fork-safe. There are many things that keep me awake at night but this is not one of them.

alfiepates commented 8 years ago

If the next Windows or FreeBSD release has a flawed PRNG, that will compromise node.js.

I don't believe this argument holds any merit, honestly.

~FreeBSD are not just going to push broken code~~ EDIT: FreeBSD are not going to push broken code to the -STABLE kernel branch, especially when they have a perfectly functional CSPRNG already, and especially as they have a security team of their own who would definitely catch any changes to the CSPRNG which would lead to a vulnerability in the OS.

I'm going to be frank, here: Attempting to justify away security issues is downright irresponsible, and possibly dangerous. You need to strive for bulletproof security, or you may as well not implement any security at all (And no, don't twist this to mean "Don't bother with security", because that's just lazy.)

I appreciate it's "extra work" to make a change like this, but considering you're using a PRNG which has actually caused security issues in the past, I'd err on the safe side and move to something more provably secure.

bnoordhuis commented 8 years ago

as they have a security team of their own who would definitely catch any changes to the CSPRNG which would lead to a vulnerability in the OS

Besides being an appeal to authority, you're asking (generic) me to trust several teams of implementors (the platforms) instead of just one (OpenSSL) to get their implementation right.

considering you're using a PRNG which has actually caused security issues in the past

Are you saying you feel OpenSSL's PRNG is insecure? If so, why aren't you taking that up with the OpenSSL team? Griping about it here isn't going to do any good.

I'll repeat what I've said above: get the upstream project to move over, and we as downstream consumers will automatically move along with it.

joepie91 commented 8 years ago

Here's a reason not to switch: OpenSSL's PRNG is a known quantity, the strength of platform-specific PRNGs is not. If the next Windows or FreeBSD release has a flawed PRNG, that will compromise node.js.

Yet OpenSSL's PRNG relies on these platform-specific PRNGs, and mixing in one broken PRNG can weaken the entire (combined) PRNG. How does relying on OpenSSL fix the issue you've described?

The bucket list of fork-safety issues that would have to be addressed is so long that I think it's safe to say that node.js will never be fork-safe. There are many things that keep me awake at night but this is not one of them.

That is a very dangerous assumption to make.

f so, why aren't you taking that up with the OpenSSL team?

I think the existence of this project should give an indication. At this stage, there's a fairly wide consensus around the security community that OpenSSL is awful software, and the only real reason it is still being recommended is because it's what has been tested in the real world for so long. Not because it is of high quality or well-maintained.

paragonie-scott commented 8 years ago

@bnoordhuis:

Here's a reason not to switch: OpenSSL's PRNG is a known quantity, the strength of platform-specific PRNGs is not. If the next Windows or FreeBSD release has a flawed PRNG, that will compromise node.js.

This is a common argument that people make, but it's ultimately invalid.

Even if you avoid depending on the operating system's PRNG, the rest of your system definitely depends on it for security. Node.js will be compromised regardless of what Node.js does.

indutny commented 8 years ago

Just a bit of FYI for everyone here:

http://lwn.net/Articles/633805/rss

"FreeBSD random number generator broken for last 4 months"

ChALkeR commented 8 years ago

@indutny Note that it also affects keys generated by Node.js on FreeBSD (using crypto.randomBytes()):

This includes, but not limited to, ssh keys and keys generated by openssl.

ircmaxell commented 8 years ago

@bnoordhuis

Here's a reason not to switch: OpenSSL's PRNG is a known quantity, the strength of platform-specific PRNGs is not. If the next Windows or FreeBSD release has a flawed PRNG, that will compromise node.js.

Actually, if Windows's CSPRNG is compromised, then Node.JS will be compromised. This is because OpenSSL relies on the system CSPRNG to seed it. There is no other source of high-quality random data in the system.

This means that the OpenSSL CSPRNG can by definition not be any stronger than the system's CSPRNG. However, it can be weaker (as has been seen several times).

The reason @paragonie-scott and others (including myself) are anti-userspace-csprng is that they provide no possible security gain, but introduce several security risks (by increasing attack surface area, by increasing bug surface area, etc).

There really is no benefit to not switching (other than not making a change at all). However, as has been demonstrated in this thread already, there are several advantages to switching.

My suggestion would be to switch to the kernel-space CSPRNG.

indutny commented 8 years ago

@ChALkeR this was a reply to:

FreeBSD are not just going to push broken code, especially when they have a perfectly functional CSPRNG already

ChALkeR commented 8 years ago

@indutny yes, this assumption by @alfiepates is incorrect:

FreeBSD are not just going to push broken code, especially when they have a perfectly functional CSPRNG already, and especially as they have a security team of their own who would definitely catch any changes to the CSPRNG which would lead to a vulnerability in the OS.

Everyone makes mistakes, you can't say «{*} are not just going to push broken code» or «this lib is magical, shining, and will never be broken».

But what should be actually considered here is the fact that OpenSSL PRNG depends on system PRNG. So, it seems like under no circuimstances you could trust OpenSSL PRNG more than system PRNG, which makes «we can't trust system PRNGs, so let's use OpenSSL» argument invalid.

alfiepates commented 8 years ago

@indutny

I will retract some of my comment, it was far too absolute, but I don't believe it's wrong.

It's disingenuous to link this article without stating the fact that this occured on the FreeBSD -CURRENT branch, as opposed to the -STABLE branch. The -CURRENT branch is the bleeding-edge branch, and therefore bugs do happen, and in this case the bug was caught before the it made it to -STABLE.

Besides, I feel you're missing the point of this discussion. OpenSSL's CSPRNG is a security risk. EDIT: Need to pay more attention when tired.

indutny commented 8 years ago

@ChALkeR of course, I'm not saying that this is a counter-argument. Just wanted to make sure that everyone on the same page and does not trust OS vendors blindly.

indutny commented 8 years ago

@alfiepates hey, I'm not missing the point here! :wink: In fact, I'm almost convinced that this should happen. See https://github.com/nodejs/node/issues/5798#issuecomment-198833767

alfiepates commented 8 years ago

@indutny Ahh, wonderful. I'll retract that part of my comment too :P (Please forgive me, I'm one cup of coffee behind right now)

indutny commented 8 years ago

There is however a risk of incorrect implementation on our side, when we will write that new code to support OS-level PRNG. Just some food for thoughts.

ChALkeR commented 8 years ago

@indutny True. There is also a blocking-vs-nonblocking argument that would need to be discussed when implementing this (i.e. /dev/random vs /dev/urandom). I don't have an opinion on that yet.

paragonie-scott commented 8 years ago

@indutny Sure. I'm almost certain that the rough draft will be incorrect in some way. That's why peer review matters.

For what it's worth, I did help with PHP's implementation and I work for a company that audits crypto code. There are others in this thread that are often more perceptive than I am.

So as long as we participated it's highly likely that any implementation bugs will be spotted and rectified.

paragonie-scott commented 8 years ago

@ChALkeR: https://blog.cr.yp.to/20140205-entropy.html

Is there any serious argument that adding new entropy all the time is a good thing? The Linux /dev/urandom manual page claims that without new entropy the user is "theoretically vulnerable to a cryptographic attack", but (as I've mentioned in various venues) this is a ludicrous argument—how can anyone simultaneously believe that

  • we can't figure out how to deterministically expand one 256-bit secret into an endless stream of unpredictable keys (this is what we need from urandom), but
  • we can figure out how to use a single key to safely encrypt many messages (this is what we need from SSL, PGP, etc.)?

There are also people asserting that it's important for RNGs to provide "prediction resistance" against attackers who, once upon a time, saw the entire RNG state. But if the attacker sees the RNG state that was used to generate your long-term SSL keys, long-term PGP keys, etc., then what exactly are we gaining by coming up with unpredictable random numbers in the future?

Also http://www.2uo.de/myths-about-urandom/

/dev/urandom is what you want here.

ChALkeR commented 8 years ago

It looks like /dev/random is too slow on some systems, so using it would be a semver-major change (and perhaps even a regression). Using /dev/urandom wouldn't be a semver-major change.

ojss commented 8 years ago

Not sure about windows and linux but OSX and FreeBSD use Yarrow, I think FreeBSD switched to Fortuna. Both the algorithms are made by the same person and are quite reliable. Better than user space generators

interesting link: Linux random number generator

MylesBorins commented 8 years ago

Chiming in from the rafters... would there be a reason this would not make sense to implement in LibUV and be a downstream consumer to their implementation?

paragonie-scott commented 8 years ago

would there be a reason this would not make sense to implement in LibUV and be a downstream consumer to their implementation?

If you can guarantee rapid Node.js adoption of this (apparently new?) API in LibUV, this would probably be better, as there may be other applications that depend on it.

bnoordhuis commented 8 years ago

This means that the OpenSSL CSPRNG can by definition not be any stronger than the system's CSPRNG.

That's not true. /dev/urandom is not the only source of entropy, especially when you have egd installed. Even when you don't, there's still a few extra bits of entropy gained by mixing in the results from getpid(), gettimeofday(), etc.

Also, and I'm repeating myself for the third time now, if you all feel OpenSSL's PRNG is so horribly broken, then why are you not taking that up with the OpenSSL project? If OpenSSL switches, we switch - what's so difficult to understand here?

alfiepates commented 8 years ago

Also, and I'm repeating myself for the third time now, if you all feel OpenSSL's PRNG is so horribly broken, then why are you not taking that up with the OpenSSL project?

That's already been addressed. OpenSSL is a generally mediocre piece of software that's only really stuck around because we're already all using it and it's slightly less awful than rolling our own code in most cases... it's not a fantastic codebase at all and where we already have great CSPRNG (/dev/urandom) there's no point using code that has caused security issues in the past.

We can sit around poking OpenSSL until they do something (which is not going to happen in a reasonable timeframe) or we can say "hey, here's an issue" and fix what we have the power to fix.

bnoordhuis commented 8 years ago

So what you're suggesting is that, contrary to decades of good software engineering practices, we fix problems at the leafs instead of the root? I think you can guess from my tone how I feel about that.

paragonie-scott commented 8 years ago

Also, and I'm repeating myself for the third time now, if you all feel OpenSSL's PRNG is so horribly broken, then why are you not taking that up with the OpenSSL project?

Okay, here you go: https://github.com/openssl/openssl/issues/898

They can read this thread for context.

bnoordhuis commented 8 years ago

It's considered good form to give a summary in the bug report but thanks, I appreciate you took the time to open one.

paragonie-scott commented 8 years ago

It's considered good form to give a summary in the bug report

Rest assured, they're well aware of the deficits in their userspace RNG. It's criticized quite frequently. I anticipate it will be swiftly closed as WONTFIX.

joepie91 commented 8 years ago

there's still a few extra bits of entropy gained by mixing in the results from getpid(), gettimeofday(), etc.

Neither of those seem particularly random or unpredictable to me?

So what you're suggesting is that, contrary to decades of good software engineering practices, we fix problems at the leafs instead of the root?

Those same decades of good software engineering practices recognize that if the root is unwilling to fix, you don't leave the problem lingering around, but go fix it at the leafs instead.

Knighton910 commented 8 years ago

+1 screen shot 2016-03-20 at 12 10 08 pm

co60ca commented 8 years ago

There is no compelling argument against using kernel sources since OpenSSL is already seeding from it. As per: https://github.com/nodejs/node/issues/5798#issuecomment-198932579 +1