简体   繁体   中英

Security of rand in ruby compared to other methods

What's the security difference between:

rand(10**100).to_s(16)
# => "a8ef61cbac3b770580cdebb55c1d2cf65cf32b5df57ef44a3bea4658ff66ac37f93b540bfb4c2ddc33e"

And RandomSecure and all the others? is rand(...).to_s(...) secure enough?

The question is never "is this secure?" The question is "is this secure enough for what I'm doing?" I don't know what you're doing.

OpenSSL::Random is cryptographically secure, but it's a bit of a pain in the ass to use. That's good because you should not be trying to write your own crypto code! Use a library.

I can say that neither Random nor Kernel#rand are cryptographically secure PRNGs . SecureRandom might be, and "might be secure" doesn't cut it. However, they are pretty good. Much better than, say, most of the rand functions that come with C's standard library.

Long story short, security is only as good as the weakest link in the chain. If you don't know enough to qualify a security question with how it's being used, the Ruby PRNGs aren't going to be that weakest link. You are. No offense meant, but security flaws are far more often programmer error than weak algorithms.


From the Random docs ...

PRNGs are currently implemented as a modified Mersenne Twister with a period of 2**19937-1.

Mersenne Twister is a solid PRNG used by many, many, many languages and libraries. It's much, much, much better than most system PRNGs.

However, MT is not cryptographically secure ! If you observe about 1000 random numbers you can predict the output. It can be made more secure, but there's no indication Ruby has done that.

But Random is better than rand ...

[Random#rand] provides the base functionality of Kernel#rand along with better handling of floating point values. These are both interfaces to Random::DEFAULT, the Ruby system PRNG.

::new will create a new PRNG with a state independent of Random::DEFAULT, allowing multiple generators with different seed values or sequence positions to exist simultaneously. Random objects can be marshaled, allowing sequences to be saved and resumed.

prng = Random.new; puts prng.rand prng = Random.new; puts prng.rand is better than puts rand because each separate instance of Random will be using a different seed (and thus a different psuedo-random pattern), whereas every call to rand is using the same seed.

If your code is using rand to generate random numbers, the attacker can assume that any random number is using the same seed and the same sequence. Then they can more quickly gather observations to guess at the seed.

If each part of your code is using its own Random.new , now the attacker has to figure out which random number goes with which seed. This makes it harder to build a sequence of random numbers, and gives the attacker less numbers to work with.


Your random number generator is only as good as its seed. Random and Kernel#rand both use...

If number is omitted, seeds the generator using a source of entropy provided by the operating system, if available (/dev/urandom on Unix systems or the RSA cryptographic provider on Windows), which is then combined with the time, the process id, and a sequence number.

/dev/urandom is pretty good these days, I don't know about Windows, but ultimately it depends on the operating system and its version.


What about SecureRandom ?

This library is an interface to secure random number generators which are suitable for generating session keys in HTTP cookies, etc.

It supports the following secure random number generators:

openssl

/dev/urandom

Win32

That doesn't tell us much. Peeking at the implementation, it's just a seed generator for OpenSSL::Random.

               # File securerandom.rb, line 51
def self.gen_random(n)
  @pid = 0 unless defined?(@pid)
  pid = $$
  unless @pid == pid
    now = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
    OpenSSL::Random.random_add([now, @pid, pid].join(""), 0.0)
    seed = Random.raw_seed(16)
    if (seed)
      OpenSSL::Random.random_add(seed, 16)
    end
    @pid = pid
  end
  return OpenSSL::Random.random_bytes(n)
end

It's making up its own seed for OpenSSL::Random out of the process id, the system clock, and whatever comes from Random.raw_seed (ie. /dev/urandom ). I really can't say if that's cryptographically secure or not, nor if it's better than just letting OpenSSL::Random.random_bytes pick its own seed.

When it comes to crypto, more can be less.

But it has some nice methods to format the random bytes .


If you want cryptographically secure PRNG, use OpenSSL::Random .

require 'openssl'
rand = OpenSSL::Random.random_bytes

This doesn't produce a number, it produces a stream of bytes which is what you really want for cryptography. If this doesn't make any sense to you, don't write crypto code (but do look into it!).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM