2015-09-02 17 views
10

Say Chciałbym wygenerować bezpieczne losową int między 0 a 27 przy użyciu:Jak wygenerować losowe int za pomocą pakietu "crypto/rand"?

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

w pakiecie "crypto/rand".

Jak to zrobić?

Naprawdę nie rozumiem, jak to działa, dlaczego nie zwraca jednego z wbudowanych int Go zamiast wskaźnika do jakiegoś dużego. Typu wewnętrznego?

EDIT:

byłoby to uznane za wystarczająco bezpieczne tokeny?

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

    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)) 
} 

Wyjście to będzie coś takiego:

qZDbuPwNQGrgVmZCU9A7FUWbp8eIfn0Z 

EwZVoQ5D5SEfdhiRsDfH6dU6tAovILCZ 

cOqzODVP0GwbiNBwtmqLA78rFgV9d3VT 
+3

Czy naprawdę potrzebujesz "bezpiecznej kryptograficznie liczby pseudolosowej"? (z doc https://godoc.org/crypto/rand) Jeśli nie, możesz użyć https://godoc.org/math/rand – HectorJ

+0

Potrzebuję go do wygenerowania bezpiecznych tokenów. Jak ten, który pokazałem powyżej. Chciałbym, aby zawierała wszystkie znaki alfabetu, w tym cyfry. – Aiden

+0

Właśnie zobaczyłem twoją edycję. Alternatywnym sposobem może być wygenerowanie naprawdę dużego losowego int (np. Między 0 a maksymalnym int64) i zakodowanie go w systemie szesnastkowym lub base32. Dodam to do mojej odpowiedzi – HectorJ

Odpowiedz

11

Jeśli generujesz bezpieczne tokeny dla identyfikatorów sesji, tokenów na okaziciela OAuth, CSRF lub podobnych: chcesz wygenerować token (najlepiej) 256 bitów (32 bajty) lub nie mniej niż 192 bity (24 bajty).

Token z wartościami pomiędzy (0-27) może być wymuszony brutalnie w czasie krótszym niż jedna sekunda i nie można go uznać za bezpieczny.

np.

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. 
    } 
} 

Wyjście base64 jest bezpieczny dla nagłówków HTTP, form, organów JSON itp

Jeśli potrzebujesz całkowitą może pomóc wyjaśnić przypadków użycia, ponieważ byłoby to dziwne dla systemu wymagać tokenów jako ints.

+0

Cześć elithrar, redagowałem moje posty. Chciałem go użyć do tworzenia tokenów dla różnych rzeczy, takich jak resetowanie hasła i klucze, które mają być rozdawane ludziom w celu rejestracji na zamkniętej stronie internetowej i tak dalej. – Aiden

+1

@Aiden W tym przypadku nie potrzebujesz liczb losowych. Dowolnie losowy ciąg (base64, hex, cokolwiek) byłby więcej niż w porządku. Mój fragment powyżej wykorzystuje 'base64.URLEncoding' do generowania ciągów, których możesz użyć w adresie URL - np. 'yourdomain.com/resetpassword? token = YW55m5hbCIGNhcm5hbCBwbGVhc3U =' obejmowałoby ten przypadek użycia. Jako wskazówka: upewnij się, że ściśle wymuszasz jednorazowe użycie i datę ważności na tokenach w twoim zapleczu (SQL, Redis itp.), Ponieważ istnieje wiele ostrych krawędzi wokół ponownego używania tokenów resetowania hasła, które nie wygasają. " prawidłowo". – elithrar

+0

Dzięki za pomoc! – Aiden

12

Oto kod roboczych:

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) 
} 

Ale aby wygenerować losowy żeton, zrobiłbym coś takiego to:

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] 
} 
+0

Zobacz również https://www.socketloop.com/tutorials/golang-how-to-generate-random-string (używając base64, więc więcej niż alfabet i liczby https://en.wikipedia.org/wiki/ Base64) – HectorJ

+1

Proponuję co najmniej 24 (jeśli nie 32) bajty dla dowolnego rodzaju tokenów. – elithrar

+1

@elithrar: dzięki, edytowane. – HectorJ

0

Jeśli potrzebujesz tylko małej liczby (tj. [0, 255]), można po prostu odczytać bajt z opakowania na 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 (przykład nie będzie działać tam, ja nie wiem, czy to działa zgodnie z przeznaczeniem lub jego błąd na plac zabaw).

+2

Jeśli chodzi o plac zabaw, problem ten został oznaczony jako naprawiony, ale w rzeczywistości nie jest: https://github.com/golang/go/issues/5096. Zostawiłem komentarz – HectorJ

Powiązane problemy