String aleatória segura em Golang usando crypto/rand

Gerar strings aleatórias extremamente útil, um recurso usado em inúmeros lugares como por exemplo criar chaves para registros em bancos de dados distribuídos sem medo de colisão entre registros, criar ID de controle de sessão, criar senhas e por ai vai.

E apesar de ser uma tarefa simples, esconde alguns truques interessantes. Na verdade computadores não são bons para criar coisas aleatórias, eles são bons para criar coisas previsíveis e isso é exatamente o que não se quer quando o objetivo é criptografia e segurança.

Por exemplo seria desastroso se alguém conseguisse adivinhar o ID de controle de sessão de um site, seria trivial capturar sessões de outros usuários do sistema. Quanto mais imprevisível for a string da chave melhor.

Felizmente Golang tem um package pronto para gerar chaves de alta qualidade. No exemplo abaixo usamos o pacote crypto/rand para gerar uma string aleatória de 10 bytes e então geramos um hash sha1 dessa string, por fim convertemos esse hash para hexa decimal para poder ser exibido.

package main

import (
	"crypto/rand"
	"crypto/sha1"
	"fmt"
)

func hash(b []byte) string {
	h := sha1.New()
	h.Write(b)
	sum := h.Sum(nil)
	armored := fmt.Sprintf("%x", sum)
	return armored
}

func randomString() (string, error) {
	b := make([]byte, 10)
	_, err := rand.Read(b)

	if err != nil {
		fmt.Printf("erro: %v", err)
		return "", err
	}

	armored := hash(b)
	return armored, err
}

func main() {
	armored, _ := randomString()
	fmt.Printf("string randomica %s\r\n", armored)
}

Teste no Golang Playground

Esse código é suficientemente bom para a maioria das aplicações, o pacote crypto/rand gera números aleatórios de alta qualidade e 10 bytes é mais que suficiente para quase tudo. Mas com alguns simples ajustes você pode criar strings muito mais fortes, basta aumentar o numero de bytes e alterar o tipo do hash para um mais adequado.

Por exemplo, hoje eu uso muito sha256 com uma string de 16 bytes. Que da um numero literalmente astronômico de possíveis combinações 2^128 ou 340 trilhões de trilhões de trilhões de possíveis combinações.

A possibilidade de uma colisão acidental ou seja de você acidentalmente escolher gerar a mesma string em um sistema é minúscula.

Outra maneira que uso muito para gerar strings aleatórias é gerar um UUID v4 usando o pacote github.com/google/uuid.

import "github.com/google/uuid"
.
.
.
ID := uuid.New().String()
fmt.Println(ID)

Usar UUID é especialmente útil se você estiver usando PostgreSQL para sua base de dados, isso porque você pode usar um campo tipo uuid e o PostgreSQL se encarrega de validar a entrada impedindo que uma string que não obedeça ao padrão do UUID entre e também internamente salva em um formato que facilita a indexação por esse campo.

Como praticamente todos os sistemas que eu faço tem o requisito de a qualquer momento virarem sistemas distribuídos usar UUID como índice do banco de dados é praticamente obrigatório.

Cesar Gimenes

Última modificação