如何使用"crypto/rand"包生成随机int?

Aid*_*den 17 go

假设我想在0到27之间生成一个安全的随机int,使用:

func Int(rand io.Reader, max *big.Int) (n *big.Int, err error)
Run Code Online (Sandbox Code Playgroud)

"crypto/rand"包中.

我该怎么办?

我真的不明白它是如何工作的,为什么它不会返回内置的Go ints而不是指向某个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))
}
Run Code Online (Sandbox Code Playgroud)

这将输出如下内容:

qZDbuPwNQGrgVmZCU9A7FUWbp8eIfn0Z

EwZVoQ5D5SEfdhiRsDfH6dU6tAovILCZ

cOqzODVP0GwbiNBwtmqLA78rFgV9d3VT
Run Code Online (Sandbox Code Playgroud)

eli*_*rar 22

如果您正在为会话ID,OAuth承载令牌,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.
    }
}
Run Code Online (Sandbox Code Playgroud)

base64输出对于标头,HTTP表单,JSON主体等是安全的.

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

  • @Aiden在这种情况下,你不需要随机整数.一个足够随机的字符串(base64,hex,无论如何)都会更好.我上面的代码片段使用`base64.URLEncoding`生成可以在URL中使用的字符串 - 例如`yourdomain.com/resetpassword?token = YW55m5hbCIGNhcm5hbCBwbGVhc3U =`将覆盖该用例.作为提示:确保在后端(SQL,Redis等)的令牌上严格执行一次使用和到期日期,因为在重新使用未过期的密码重置令牌时会有很多尖锐的边缘"正确的". (2认同)

Hec*_*orJ 20

这是一些工作代码:

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)
}
Run Code Online (Sandbox Code Playgroud)

但要生成随机令牌,我会做这样的事情:

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]
}
Run Code Online (Sandbox Code Playgroud)

  • 对于任何类型的令牌,我建议至少使用 24(如果不是 32)字节。 (2认同)