問題描述
我的團隊收到了一些生成隨機令牌的服務器端代碼(Java 中),我對此有疑問 -
My team got handed over some server side code (in Java) that generates random tokens and I have a question regarding the same -
這些令牌的用途相當敏感 - 用于會話 ID、密碼重置鏈接等.因此它們確實需要加密隨機以避免有人猜測它們或暴力破解它們是可行的.令牌是長"的,所以它是 64 位長.
The purpose of these tokens is fairly sensitive - used for session id, password reset links etc. So they do need to be cryptographically random to avoid somebody guessing them or brute force them feasibly. The token is a "long" so it is 64 bits long.
代碼當前使用 java.util.Random
類來生成這些標記.java.util 的 文檔.Random
明確說明如下:
The code currently uses the java.util.Random
class to generate these tokens. The documentation for java.util.Random
clearly states the following:
java.util.Random 的實例不是加密安全的.考慮改為使用 SecureRandom 來獲取加密安全的偽隨機數生成器,以供對安全敏感的應用程序使用.
Instances of java.util.Random are not cryptographically secure. Consider instead using SecureRandom to get a cryptographically secure pseudo-random number generator for use by security-sensitive applications.
但是,代碼當前使用 java.util.Random
的方式是這樣的 - 它實例化 java.security.SecureRandom
類,然后使用 SecureRandom.nextLong()
方法獲取用于實例化 java.util.Random
類的種子.然后它使用 java.util.Random.nextLong()
方法生成令牌.
However, the way the code is currently using java.util.Random
is this - It instantiates the java.security.SecureRandom
class and then uses the SecureRandom.nextLong()
method to obtain the seed that is used for instantiating the java.util.Random
class. Then it uses java.util.Random.nextLong()
method to generate the token.
所以我現在的問題 - 考慮到 java.util.Random
正在使用 java.security.SecureRandom
播種,它仍然不安全嗎?我是否需要修改代碼以便它專門使用 java.security.SecureRandom
來生成令牌?
So my question now - Is it still insecure given that the java.util.Random
is being seeded using java.security.SecureRandom
? Do I need to modify the code so that it uses java.security.SecureRandom
exclusively to generate the tokens?
目前代碼種子是 Random
在啟動時一次
Currently the code seed's the Random
once at startup
推薦答案
標準的 Oracle JDK 7 實現使用所謂的線性同余生成器在 java.util.Random
中生成隨機值.
The standard Oracle JDK 7 implementation uses what's called a Linear Congruential Generator to produce random values in java.util.Random
.
取自 java.util.Random
源代碼 (JDK 7u2),來自對方法 protected int next(int bits)
的注釋,該方法是生成隨機值:
Taken from java.util.Random
source code (JDK 7u2), from a comment on the method protected int next(int bits)
, which is the one that generates the random values:
這是一個線性同余偽隨機數生成器,如由 D. H. Lehmer 定義并由 Donald E. Knuth 在計算機編程的藝術, 第 3 卷:半數值算法,第 3.2.1 節.
This is a linear congruential pseudorandom number generator, as defined by D. H. Lehmer and described by Donald E. Knuth in The Art of Computer Programming, Volume 3: Seminumerical Algorithms, section 3.2.1.
線性同余生成器的可預測性
Hugo Krawczyk 寫了一篇關于如何預測這些 LCG 的非常好的論文(如何預測同余生成器").如果您幸運且感興趣,您仍然可以在網絡上找到它的免費、可下載版本.還有大量研究清楚地表明,您應該永遠不要將 LCG 用于安全關鍵目的.這也意味著您的隨機數現在是可預測的,這是您不希望會話 ID 等的東西.
Predictability of Linear Congruential Generators
Hugo Krawczyk wrote a pretty good paper about how these LCGs can be predicted ("How to predict congruential generators"). If you're lucky and interested, you may still find a free, downloadable version of it on the web. And there's plenty more research that clearly shows that you should never use an LCG for security-critical purposes. This also means that your random numbers are predictable right now, something you don't want for session IDs and the like.
攻擊者必須等待 LCG 在一個完整周期后重復的假設是錯誤的.即使使用最佳循環(其遞推關系中的模數 m),也很容易在比完整循環更短的時間內預測未來值.畢竟,這只是一堆需要求解的模方程,只要你觀察到足夠多的 LCG 輸出值,就變得容易了.
The assumption that an attacker would have to wait for the LCG to repeat after a full cycle is wrong. Even with an optimal cycle (the modulus m in its recurrence relation) it is very easy to predict future values in much less time than a full cycle. After all, it's just a bunch of modular equations that need to be solved, which becomes easy as soon as you have observed enough output values of the LCG.
更好"的種子不會提高安全性.如果您使用 SecureRandom
生成的隨機值作為種子,或者甚至通過擲骰子數次來產生該值,這根本無關緊要.
The security doesn't improve with a "better" seed. It simply doesn't matter if you seed with a random value generated by SecureRandom
or even produce the value by rolling a die several times.
攻擊者將簡單地根據觀察到的輸出值計算種子.在 java.util.Random
的情況下,這比 2^48 花費的時間明顯少.不信的人可以試試這個實驗,它表明你可以預測未來的 Random
輸出,在大約 2^16 的時間內僅觀察兩個(!)輸出值.現在,在現代計算機上預測隨機數的輸出甚至不需要一秒鐘.
An attacker will simply compute the seed from the output values observed. This takes significantly less time than 2^48 in the case of java.util.Random
. Disbelievers may try out this experiment, where it is shown that you can predict future Random
outputs observing only two(!) output values in time roughly 2^16. It takes not even a second on a modern computer to predict the output of your random numbers right now.
替換您當前的代碼.僅使用 SecureRandom
.那么至少你會有一點保證,結果是很難預測的.如果您想要加密安全 PRNG 的屬性(在您的情況下,這就是您想要的),那么您只需要使用 SecureRandom
.聰明地改變它應該使用的方式幾乎總是會導致一些不太安全的東西......
Replace your current code. Use SecureRandom
exclusively. Then at least you will have a little guarantee that the result will be hard to predict. If you want the properties of a cryptographically secure PRNG (in your case, that's what you want), then you have to go with SecureRandom
only. Being clever about changing the way it was supposed to be used will almost always result in something less secure...
這篇關于java.util.Random 和 java.security.SecureRandom 之間的區別的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!