Aula 09 - Golang - Fiber - Register User

Nessa aula vamos adicionar o pacote bcrypt para poder fazer o hash do password do usuário, para isso acesse: pkg.go.dev E pesquise bcrypt, aí então é só escolher a primeira opção que aparece.

Instalando bcrypt no projeto

Se o projeto tiver rodando, pare ele e instale com:

go get -u golang.org/x/crypto/bcrypt
Bcrypt é um padrão da indústria testado e comprovado para resistir a ameaças de hackers e outros agentes mal-intencionados. O que ele faz é aplicar uma funçao de hash na senha em texto puro, para depois, na hora de armazenar, salvar no banco só o hash gerado e não a senha original. A ideia por trás do hash de senhas dos usuários, é deixar as passwords seguras contra ataques de hackers, ou qualquer tipo de invasão de sistema, enfim, qualquer ação maliciosa. Há 3 formas que as empresas armazenam as passwords: texto puro(plain text), encriptografando a senha, ou usando uma função de hash.

Texto puro

Texto puro é obviamente a maneira mais perigosa de armazenar senhas. Se hackers violarem o banco de dados da empresa, eles conseguem ver todas as senhas dos usuários. Muitas pessoas têm o mau hábito de usar a mesma senha para várias contas, é provável que 1 senha comprometida pode levar a mais contas comprometidas.

Encriptografando a senha

Uma alternativa possível ao armazenamento de texto puro é a criptografia. As empresas pegam as senhas dos usuários e antes de armazenar, criptografa elas com uma chave. Isso impediria que hackers obtivessem as senhas reais dos usuários, mas ainda é muito arriscado. Na camada de criptografia ainda é uma senha de texto puro e por isso se o invasor consegue roubar a chave de criptografia também, ele pode desbloquear todas as senhas. A criptografia foi projetada para funcionar de duas maneiras: você pode criptografar as senhas de um usuário para manter ela segura, mas, você também pode descriptografá-la para revelar a senha novamente. Isso é muito prático quando você quer compartilhar dados de forma segura, mas não é bom se você quer impedir que invasores violem sua senha. Isso nos leva à terceira técnica de armazenar senhas que é usando função de hash.

Função de Hash

Como funciona? As funções hash pega uma entrada, que pode ser um texto como senha ou um arquivo, e transforma em uma string de texto sempre com o mesmo comprimento. Existem muitas funções diferentes de hash disponíveis: MD4, MD5, SHA-1, WHIRLPOOL, etc. As funções de hash são muito diferentes da criptografia porque elas só funcionam unidirecionamente. Você pode calcular o hash de uma senha, mas você não pode pegar um hash e transformá-lo de volta aos dados originais. Ao usar hashes, as empresas podem verificar se você está logando com a senha correta, sem ter que armazenar sua senha real. No entanto, elas não são perfeitas também. A maioria dos algoritmos de hashing são otimizados para velocidade, quanto mais hashes por segundo, melhor e mais seguro é o hash que elas podem calcular. Mas de qualquer forma, existe uma vulnerabilidade a ataques de força bruta. O hacker pode simplesmente tentar calcular todas as senhas possíveis, e conseguir reverter o hash. Uma GPU moderna pode fazer isso com uma velocidade de 292 milhões de hashes por segundo (292,2 MH/s), é só uma questão de tempo antes de uma senha hash ser quebrada usando essa técnica. E se isso não for rápido o suficiente, hackers também podem usar rainbow table para acelerar o processo. Estas são listas de hashes pré-computados que podem ser usadas para encontrar rapidamente senhas fracas e comumente usadas. Veja na ilustração abaixo como funciona a função de hash. Veja a tabela abaixo, mostrando a aplicação da função de hash MD5 nas senhas: a, ab, abc, e abc1. Perceba que o hash gerado é sempre do mesmo tamanho, independente do tamanho da senha. Vamos importar o bcrypt e usar para fazer o hash da senha com o custo de 14 para gerar o hash.

fiber-project/controllers/authController.go  


package controllers

import (
	"fiber-project/models"

	"github.com/gofiber/fiber/v2"
	"golang.org/x/crypto/bcrypt"
)

func Register(c *fiber.Ctx) error {
	var data map[string]string

	if err := c.BodyParser(&data); err != nil {
		return err
	}
	if data["password"] != data["confirm_password"] {
		c.Status(400)
		return c.JSON(fiber.Map{
			"message": "Passwords do not match!",
		})
	}

	password, _ := bcrypt.GenerateFromPassword([]byte(data["password"]), 14)

	user := models.User{
		FirstName: data["first_name"],
		LastName:  data["last_name"],
		Email:     data["email"],
		Password:  password,
	}

	return c.JSON(user)
}

Temos que trocar o campo Password de string para []byte no modelo user.

fiber-project/models/user.go  


package models

type User struct {
	Id        uint
	FirstName string
	LastName  string
	Email     string `gorm:"unique"`
	Password  []byte
}
Nesse ponto podemos fazer um teste com o postman, no endpoint http://localhost:8000/api/register Saída: {   "Id": 0,   "FirstName": "a",   "LastName": "a",   "Email": "a@mail.com",   "Password": "JDJhJDE0JC44TG9tVGRzb29GeGQxUWJpcW9MaWUuZFJiWThFaFo0V2lMOWdWQjAzL1NmSTJuc2s2aFJ5" } E fazer uma request POST e vê se a saída mostra a senha depois de passar pela função hash. Agora vamos salvar a senha no banco. Para isso, vamos precisar do fiber-project/database/connect.go dentro do fiber-project/controllers/authController.go. Para fazer isso, vamos criar uma variável chamada DB, e ela será um ponteiro para o *gorm.DB que é o drive para acesso ao banco A variável connection ela abre a conexão com o mysql, e o drive que tá referenciado pela variável DB, vai apontar para a connection.

fiber-project/database/connect.go


package database

import (
	"fmt"

	"fiber-project/models"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var DB *gorm.DB

func Connect() {
	var dsn = "toticavalcanti:mysql1234@/fluent_admin?charset=utf8mb4&parseTime=True&loc=Local"
	var v = "Não conseguiu conectar ao banco de dados"
	connection, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

	if err != nil {
		panic(v)
	}

	DB = connection

	connection.AutoMigrate(&models.User{})
	fmt.Println("Conexão OK!")
}

Ponteiros

Exemplo de uso de ponteiro

Agora podemos salvar o usuário e sua senha depois de passar pela função de hash.

fiber-project/controllers/authController.go  


package controllers

import (
	"fiber-project/database"
	"fiber-project/models"

	"github.com/gofiber/fiber/v2"
	"golang.org/x/crypto/bcrypt"
)

func Register(c *fiber.Ctx) error {
	var data map[string]string

	if err := c.BodyParser(&data); err != nil {
		return err
	}
	if data["password"] != data["confirm_password"] {
		c.Status(400)
		return c.JSON(fiber.Map{
			"message": "Passwords do not match!",
		})
	}

	password, _ := bcrypt.GenerateFromPassword([]byte(data["password"]), 14)

	user := models.User{
		FirstName: data["first_name"],
		LastName:  data["last_name"],
		Email:     data["email"],
		Password:  password,
	}

	database.DB.Create(&user)

	return c.JSON(user)
}

Teste novamente usando o Postman. { "Id": 1, "FirstName": "a", "LastName": "a", "Email": "a@mail.com", "Password": "JDJhJDE0JC44TG9tVGRzb29GeGQxUWJpcW9MaWUuZFJiWThFaFo0V2lMOWdWQjAzL1NmSTJuc2s2aFJ5" } 

Nos vemos na próxima então ;)

Até lá!

Código da aula: Github

Redes Sociais:

facebook          

Novamente deixo meus link de afiliados:

Hostinger

Digital Ocean

One.com

Obrigado, até a próxima e bons estudos. ;)