Algoritmos

Hasheando y comprobando contraseñas en Golang

Introducción

Seguimos con los tutoriales de Go. Ahora veremos cómo encriptar una contraseña y luego comprobar si esa contraseña coincide.

Sigue leyendo para que te des una mejor idea.

¿Cómo funciona?

En todos nuestros sistemas en donde implementemos contraseñas, debemos guardarlas de tal forma que sean, como yo lo llamo, “de un sólo camino”. Es decir, que una vez encriptadas ya nada las pueda desencriptar.

¿y cómo las compruebo después, si no las puedo desencriptar?

Muy fácil, supongamos que tenemos un algoritmo muy complejo que las encripta. Entonces, al registrar la contraseña, algo como “123” se convierte en “aaa”.

Dicho valor ya no podrá ser desencriptado, así que cuando el usuario quiera iniciar sesión, no podremos comprobar si su contraseña al desencriptar se convierte en “aaa”.

Pero hay algo que sí podemos hacer, encriptar la contraseña que nos está mandando, y comprobarla con la contraseña encriptada que ya tenemos guardada.

Así que si en nuestra base de datos tenemos “aaa” y el usuario quiere entrar con la contraseña “123” la encriptamos y al encriptarla se convertirá en “aaa” el cual es el mismo valor que tenemos.

Si en cambio el usuario intenta entrar con la contraseña “456” que al encriptarla se convierte en “bbb” entonces no coincidirá.

Obviamente esto fue un ejemplo, en la vida real las contraseñas se encriptan de diferente manera, pero la teoría es casi la misma.

Instalar paquete

No viene incluido por defecto, así que lo instalamos:

go get golang.org/x/crypto/bcrypt

Con esto ya podemos usar a bcrypt.

Hashear contraseña en Go

Es muy fácil generar un hash de contraseña segura. Para ello llamamos al método GenerateFromPassword de bcrypt.

Como primer argumento le pasamos un arreglo de bytes con la contraseña en texto plano, y como segundo le pasamos el costo.

Sobre el costo

El costo es muy importante a la hora de hashear nuestras contraseñas. Uno muy alto (el límite es 31) es casi imposible de romper, pero tomará mucho tiempo hashear una contraseña.

Por eso Go propone un costo mínimo, uno por defecto y uno máximo. El mínimo es 4, el que es por defecto es 10 y el máximo 31.

Recomiendo usar el 10, como se verá en el código. Pero si estamos dispuestos a que se consuman más recursos, podemos establecerlo más alto.

Cabe mencionar que también será pesado comprobar un hash cuando el usuario quiera loguearse, así que hay que establecer bien el costo.

Y finalmente, si pasas un costo menor que el mínimo (por ejemplo, 3 o 2) se pondrá el costo por defecto (10).

Código de ejemplo

El siguiente código hashea la contraseña “123” con un costo de 10.

package main

import (
 "fmt"

 "golang.org/x/crypto/bcrypt"
)

func main() {
 contraseñaPlana := "123"
 contraseñaPlanaComoByte := []byte(contraseñaPlana)
 hash, err := bcrypt.GenerateFromPassword(contraseñaPlanaComoByte, bcrypt.DefaultCost) //DefaultCost es 10
 if err != nil {
  fmt.Println(err)
 }
 hashComoCadena := string(hash)
 fmt.Printf("El hash generado a partir de %s es %s\n", contraseñaPlana, hashComoCadena)
}

Como se aprecia, debemos convertir la cadena a un arreglo de bytes. Y luego, como GenerateFromPassword regresa igualmente un arreglo de bytes lo convertimos a cadena.

Si lo ejecuto, esto pasa:

Hashear contraseña en Go

Si tú lo ejecutas te dará un resultado diferente, pero tranquilo que es normal y es una excelente práctica, ya que así se previenen los ataques de diccionario.

El hash como cadena ya podemos guardarlo en una base de datos o en donde queramos usarlo. Ahora veamos cómo comparar si la contraseña coincide.

Comprobar si hash y contraseña en texto plano coinciden

Ahora veamos cómo comprobar si una contraseña en texto plano y nuestro hash coinciden. Para ello pongamos el ejemplo de que nuestro hash es $2a$10$yXR.Jd79OMcHfNK8zZU5N.nQ7ZL5N1d65sGIbPr.vIf6Q3o7e540i (como en la imagen de arriba) y lo vamos a comprobar primero con “555” (contraseña incorrecta) y luego con “123” (correcta).

Go provee el método CompareHashAndPassword que recibe 2 argumentos de tipo arreglo de bytes: el hash y la contraseña en texto plano.

Importante: esta función no devuelve un booleano, sino que devuelve un error (o nil en caso de que no haya error). Si devuelve nil, entonces las contraseñas conciden. Si devuelve error, entonces las contraseñas no coinciden.

Código de ejemplo

Primero probemos con este, en donde la contraseña es incorrecta:

package main

import (
 "fmt"

 "golang.org/x/crypto/bcrypt"
)

func main() {
 hash := "$2a$10$yXR.Jd79OMcHfNK8zZU5N.nQ7ZL5N1d65sGIbPr.vIf6Q3o7e540i"
 hashComoByte := []byte(hash)
 contraseña := "555"
 contraseñaComoByte := []byte(contraseña)
 error := bcrypt.CompareHashAndPassword(hashComoByte, contraseñaComoByte)
 if error == nil {
  fmt.Println("Las contraseñas coinciden :)")
 } else {
  fmt.Println("Las contraseñas no coinciden. El error es: ", error)
 }
}

Si nos fijamos, dijimos que la contraseña es 555 pero realmente es 123. Al ejecutar el programa:

Comprobar hash y contraseña

Es importante conocer el error, para ello lo estamos imprimiendo. Y como podemos ver, dice que la contraseña hasheada no es el hash de la contraseña en texto plano que proporcionamos.

Ahora cambiaré la contraseña a 123:

package main

import (
 "fmt"

 "golang.org/x/crypto/bcrypt"
)

func main() {
 hash := "$2a$10$yXR.Jd79OMcHfNK8zZU5N.nQ7ZL5N1d65sGIbPr.vIf6Q3o7e540i"
 hashComoByte := []byte(hash)
 contraseña := "123"
 contraseñaComoByte := []byte(contraseña)
 error := bcrypt.CompareHashAndPassword(hashComoByte, contraseñaComoByte)
 if error == nil {
  fmt.Println("Las contraseñas coinciden :)")
 } else {
  fmt.Println("Las contraseñas no coinciden. El error es: ", error)
 }
}

Y si ejecuto el código:

Hash y contraseña coinciden

Así es como finalizamos este post.

Conclusión

Vimos las formas básicas de hashear y comprobar contraseñas. Recuerda que esto es un ejemplo fácil para no confundir al lector, pero puedes tomar atajos o crear tus propias funciones dependiendo de tus necesidades.

Finalmente aquí dejo la documentación oficial: bcrypt – GoDoc

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

2 días hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

1 semana hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

1 semana hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

1 semana hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

1 semana hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

1 semana hace

Esta web usa cookies.