【问题标题】:How can I generate a random int using the "crypto/rand" package?如何使用“crypto/rand”包生成随机整数?
【发布时间】:2015-09-03 07:56:16
【问题描述】:

假设我想生成一个介于 0 到 27 之间的安全随机整数:

func Int(rand io.Reader, max *big.Int) (n *big.Int, err error)

"crypto/rand" 包中。

我该怎么做?

我不太明白这是如何工作的,为什么它不返回内置的 Go 整数之一而不是指向某个 big.Int 类型的指针?

编辑:

这对于令牌来说是否足够安全?

func getToken(length int) string {
    token := ""
    codeAlphabet := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    codeAlphabet += "abcdefghijklmnopqrstuvwxyz"
    codeAlphabet += "0123456789"

    for i := 0; i < length; i++ {
        token += string(codeAlphabet[cryptoRandSecure(int64(len(codeAlphabet)))])
    }
    return token
}

func cryptoRandSecure(max int64) int64 {
    nBig, err := rand.Int(rand.Reader, big.NewInt(max))
    if err != nil {
        log.Println(err)
    }
    return nBig.Int64()
}

func main() {
    fmt.Println(getToken(32))
}

这将输出如下内容:

qZDbuPwNQGrgVmZCU9A7FUWbp8eIfn0Z

EwZVoQ5D5SEfdhiRsDfH6dU6tAovILCZ

cOqzODVP0GwbiNBwtmqLA78rFgV9d3VT

【问题讨论】:

  • 你真的需要一个“密码安全的伪随机数”吗? (来自文档godoc.org/crypto/rand)如果你不这样做,你可以使用godoc.org/math/rand
  • 我需要它来生成安全令牌。就像我上面展示的那样。我希望它可以包含字母表中的所有字符,包括数字。
  • 刚刚看到您的编辑。另一种方法可能是生成一个非常大的随机 int(如 0 和最大 int64 之间)并将其编码为十六进制或 base32。我会把它添加到我的答案中
  • 通常,如果有人需要一个加密强的随机数,她不需要 [0,27] 的范围(即 5 位),而是大得多的范围,更像2048 位,不适合 int64。所以返回了一个 big.int。也许您应该生成一个长比特序列并简单地对其进行 base32 编码?
  • “加密安全 PRNG”和“5 位”不能一起使用。至少一个令牌(例如,用于 CSRF 或会话 ID)应该是 256 位。我会重新考虑你的方法。

标签: go


【解决方案1】:

这是一些工作代码:

package main

import (
    "fmt"
    "crypto/rand"
    "math/big"
)

func main() {
    nBig, err := rand.Int(rand.Reader, big.NewInt(27))
    if err != nil {
        panic(err)
    }
    n := nBig.Int64()
    fmt.Printf("Here is a random %T in [0,27) : %d\n", n, n)
}

但是要生成一个随机令牌,我会这样做:

package main

import (
    "crypto/rand"
    "encoding/base32"
    "fmt"
)

func main() {
    token := getToken(10)
    fmt.Println("Here is a random token : ", token)
}

func getToken(length int) string {
    randomBytes := make([]byte, 32)
    _, err := rand.Read(randomBytes)
    if err != nil {
        panic(err)
    }
    return base32.StdEncoding.EncodeToString(randomBytes)[:length]
}

【讨论】:

【解决方案2】:

如果您要为会话 ID、OAuth Bearer 令牌、CSRF 或类似内容生成安全令牌:您希望生成(理想情况下)256 位(32 字节)或不少于 192 位(24 字节)的令牌。

值在 (0-27) 之间的令牌可以在不到一秒的时间内被暴力破解,并且不能被认为是安全的。

例如

package main

import (
    "crypto/rand"
    "encoding/base64"
)

// GenerateRandomBytes returns securely generated random bytes.
// It will return an error if the system's secure random
// number generator fails to function correctly, in which
// case the caller should not continue.
func GenerateRandomBytes(n int) ([]byte, error) {
    b := make([]byte, n)
    _, err := rand.Read(b)
    // Note that err == nil only if we read len(b) bytes.
    if err != nil {
        return nil, err
    }

    return b, nil
}

// GenerateRandomString returns a URL-safe, base64 encoded
// securely generated random string.
func GenerateRandomString(s int) (string, error) {
    b, err := GenerateRandomBytes(s)
    return base64.URLEncoding.EncodeToString(b), err
}

func main() {
    // Example: this will give us a 44 byte, base64 encoded output
    token, err := GenerateRandomString(32)
    if err != nil {
        // Serve an appropriately vague error to the
        // user, but log the details internally.
    }
}

base64 输出对于标头、HTTP 表单、JSON 正文等是安全的。

如果您需要一个整数,它可能有助于解释您的用例,因为系统要求将令牌作为整数是很奇怪的。

【讨论】:

  • 嗨,elithrar,我编辑了我的帖子。我想用它为不同的事情创建令牌,例如密码重置和分发给人们注册封闭网站的密钥等等。
  • @Aiden 在这种情况下,您不需要随机整数。一个足够随机的字符串(base64,十六进制,等等)会更好。我上面的 sn-p 使用 base64.URLEncoding 生成可以在 URL 中使用的字符串 - 例如yourdomain.com/resetpassword?token=YW55m5hbCIGNhcm5hbCBwbGVhc3U= 将涵盖该用例。提示:确保您在后端(SQL、Redis 等)中的令牌上严格执行一次性使用和到期日期,因为重用不会过期的密码重置令牌有很多尖锐的边缘“正确”。
【解决方案3】:

如果您只需要一个小数字(即 [0, 255]),您可以从包的 Reader 中读取一个字节:

b := []byte{0}
if _, err := rand.Reader.Read(b); err != nil {
    panic(err)
}
n := b[0]
fmt.Println(n)

Playground:http://play.golang.org/p/4VO52LiEVh(该示例在那里不起作用,我不知道它是按预期工作还是 Playground 错误)。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2020-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-29
  • 1970-01-01
相关资源
最近更新 更多