关于随机数的一点理解

  在日常的工作中,经常会遇到关于随机数的问题。在此写一点随笔。欢迎指正。


  随机数会在系统中会频繁使用,例如验证码、订单ID、密钥···。在Java中常使用的随机数生成方法有RandomSecureRandom

 

  但是通常使用在安全场景下的随机数,如生成CSRF-Tokensalt等,对随机数的随机性的要求很高,不然结果很容易预测,因此可能导致被攻击者击中。

1.1 random

  Random一个简单的举例如下图,使用random的共有方法nextInt,生成了一个0~5之间的随机数。

 

  Random是一个构造函数,参数可以传一个long类型的值,当传参为空构造的时候,实际上是使用System.nanoTime()也就是当前时间毫秒数的值为种子。

  未指定种子时,每次运行下面代码生成不同的随机数:

 

  指定种子为666,每次运行代码,得到的随机数都相同:

 

 

 

 

  由此可见,种子的设置极为重要,将直接影响最终的随机数生成。

  虽然可以使用系统时间作为种子,然后通过算法得出最终的随机数,但这其实是一种伪随机数。若种子相同,得出来的随机数也是相同的。在安全场景的使用下是比较危险的一件事。若指定种子,也要保证种子生成的随机性。

 

  一个典型的例子,如果使用Random生成一个以时间为种子的随机数,事实上很容易通过上一个产生的随机数来推断下一个随机数。

 

1.2 SecureRandom

  上面说到,random生成随机数时,若想生成真随机数,就要指定随机的种子。

  现在看下SecureRandom产生随机数,一个简单的用法:

 

   

 

 

 

  SecureRandom使用的种子,取决于系统默认的随机源是什么

  系统默认的随机源取决于$JAVA_HOME/jre/lib/security/java.security配置中的securerandom.source属性。例如jdk1.8中该配置为:

        

 

 

  使用无参构造函数实例化SecureRandom,在大多数系统中,默认的算法是“nativePRNG”(两种随机数算法,NativePRNGSHA1PRNGdev/[u]random两者之一就是NativePRNG,否则就是SHA1PRNG。),从/dev/random获取随机数。/dev/random是Linux操作系统的非物理真随机数产生设备接口。

 

  虽然SecureRandomRandom类似,如果种子一样,产生的随机数也一样: 因为种子确定,随机数算法也确定,因此输出是确定的。

  但是SecureRandom类收集了一些随机事件,比如鼠标点击,键盘点击等等,SecureRandom 使用这些随机事件作为种子。这意味着,种子是不可预测的,而不像Random默认使用系统当前时间的毫秒数作为种子,有规律可寻。

 

总结:

不是安全场景的随机数,使用Random就好。

SecureRandom的由由于种子的随机性,更实用在安全场景下。

posted @ 2020-05-03 17:40  zplsecurity  阅读(517)  评论(0)    收藏  举报