¿Qué es cifrar?

Cifrar es ocultar una determinada información para que solo la pueda consumir la persona o grupo de personas que el emisor desea.

Las técnicas más utilizadas hoy en día son:

  1. Simétrica:

La criptografía simétrica solo utiliza una clave para cifrar y descifrar el mensaje, que tiene que conocer el emisor y el receptor previamente, y este es el punto débil del sistema, la comunicación de las claves entre ambos sujetos, ya que resulta más fácil interceptar una sola clave.

2.Asimétrica:

La criptografía asimétrica se basa en el uso de dos claves: la pública (que se podrá difundir sin ningún problema a todas las personas que necesiten mandarte algo cifrado) y la privada (que no debe de ser revelada nunca).

Este es el concepto que forma la base de PKI (infraestructura de clave pública), que es el modelo de confianza que sustenta SSL / TLS.

  1. Híbrida:

Este sistema es la unión de las ventajas de los dos anteriores, debemos de partir que el problema de ambos sistemas criptográficos es que el simétrico es inseguro y el asimétrico es lento. El proceso para usar un sistema criptográfico híbrido es el siguiente (para enviar un archivo):

  • Generar una clave pública y otra privada (en el receptor).
    • Cifrar un archivo de forma síncrona.
    • El receptor nos envía su clave pública.
    • Ciframos la clave que hemos usado para encriptar el archivo con la clave pública del receptor.
    • Enviamos el archivo cifrado (síncronamente) y la clave del archivo cifrada (asíncronamente y solo puede ver el receptor).

¿Qué es el cifrado hash?

El hash es un algoritmo que se realiza en datos; como un archivo, un mensaje o una contraseña. 

Un hash generalmente se muestra como un número hexadecimal y es importante recalcar que, a diferencia de los cifrados simétricos y asimétricos, esta no se puede revertir con otro algoritmo de descifrado

¿Para qué se utiliza?

El hash se utiliza para verificar que los datos no se modifiquen, manipulen ni corrompan.

Un punto clave sobre un hash es que no importa cuántas veces ejecute el algoritmo de hash contra los datos, el hash siempre será el mismo si los datos son los mismos.

Los hashes se crean al menos dos veces para poder compararlos como en las contraseñas, por ejemplo, que a menudo se almacenan como hashes.

¿Cómo funciona?

El método hash recoge una cadena determinada que el usuario haya insertado y realiza un algoritmo sobre ella.

Los algoritmos hash son:

  • MD5

Message Digest 5 (MD5) es un algoritmo hash común que produce un hash de 128 bits. 

  • SHA

El algoritmo de hash seguro (SHA) tiene varias variaciones de SHA agrupadas en cuatro familias: SHA-0, SHA-1, SHA-2 y SHA-3.

  • HMAC

Un HMAC es una cadena de bits de longitud fija similar a otros algoritmos hash como MD5 y SHA-1 (conocidos como HMAC-MD5 y HMAC-SHA1).

Si quieres saber más consulta la página https://cybersecurityglossary.com/hashing .

Vulnerabilidades

Este método es vulnerable a exploits (ComputeHash) lo que quiere decir que nuestras contraseñas, no están seguras 100%.

Tambien es vulnerable a la técnica Cipher-Block-Chaining (CBC),que según Microsoft ya no es seguro descifrar datos cifrados con el modo Cipher-Block-Chaining (CBC) de cifrado simétrico sin garantizar primero la integridad del texto cifrado, excepto en circunstancias muy específicas.

Por lo tanto, para tener un cifrado óptimo, deberíamos utilizar los últimos objetos de la plataforma dónde estemos programando.

En nuestro caso Visual Studio además de seguir una serie de reglas de cifrado para complicar todo lo posible al Hacker:

  • Utilizar cifrados mínimos de 128 bytes
  • Se debe utilizar un número N de iteraciones cifradas.
  • Aplicar un Salt.

¿Qué es un Salt?

Un salt es un texto aleatorio que se incluye junto a la contraseña a cifrar.

Esencialmente, es un valor único que se puede dar a la contraseña para crear un valor hash diferente. Esto agrega una capa de seguridad al proceso de hash, específicamente contra ataques de fuerza bruta. Un ataque de fuerza bruta es cuando una computadora o botnet intenta todas las combinaciones posibles de letras y números hasta que encuentra la contraseña.

Caso Práctico

Trabajando en Visual Studio lo primero que tenemos que hacer es instalar los NuGets.

NuGets

A continuación, mapearemos la tabla de la base de datos

[Table("CYPHERHASH")]

   public class Usuario

   {

       [Key]

       [DatabaseGenerated(DatabaseGeneratedOption.None)]

       [Column(«IdUser»)]

       public int idUser { get; set; }

       [Column(«Nombre»)]

       public string name { get; set; }

       [Column(«Usuario»)]

       public string user { get; set; }

       [Column(«Pswd»)]

       public byte[] pswd { get; set; }

       [Column(«Salt»)]

       public string salt { get; set; }

   }

Creamos una nueva carpeta llamada Data y un nuevo contexto llamado CypherHashContext

Snippet

namespace CifradoHash.Data
{
    public class CypherHashContext:DbContext
    {
        public CypherHashContext
       (DbContextOptions<CypherHashContext> options) : base(options) { }
 
        public DbSet<Usuario> Usuarios { get; set; }
    }
}

Creamos una nueva carpeta llamada Helpers e incluimos una clase llamada CypherService, que será la encargada de realizar el cifrado de los contenidos.

Devolveremos el cifrado en formato byte[] para la base de datos, aunque también podríamos almacenar tipo nvarchar si quisiéramos.

public class CypherService
   {
       public static string GetSalt()
       {
           Random rnd = new Random();
           string salt = "";
           for (int i 1<= 50i++)
           {
               int aleat rnd.Next(0, 255);
               char letra Convert.ToChar(aleat);
               salt += letra;
           }
           return salt;
       }
 
       public static byte[] CypherHashefficent(string contenido, string salt)
       {
 
           String contenidoSalt = contenido + salt;
           SHA256Managed sha = new SHA256Managed();
           byte[] output;
           output = Encoding.UTF8.GetBytes(contenidoSalt);
           for (int i = 1; i <= 100; i++)
           {
               //realizamos el cifrado n veces
               output sha.ComputeHash(output);
           }
           sha.Clear();
           return output;
       }

Using necesario para el objeto SHA256Managed:

UsingSecurityCryptography

Using necesario para el metodo Encoding:

usingEncoding

A continuación, dentro de la carpeta Helpers, creamos una clase que será la encargada de comparar dos Arrays, nos servirá como herramienta en nuestro entorno de programación. 

Creamos una clase llamada Toolkit

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading.Tasks;
namespace CypherHash.Helpers
{
    public static class ToolKit
    {
        public static bool ArraysComparative(byte[] a, byte[] b)
        {
            bool equals = true;
            if (a.Length != b.Length)
            {
                return false;
            }
            else
            {
                for (int i = 0; i < a.Length; i++)
                {
                    if (a[i].Equals(b[i]) == false)
                    {
                        equals = false;
                        break;
                    }
                }
            }
            return equals;
        }
        // Convert an object to a byte array
        public static byte[] ObjectToByteArray(Object obj)
        {
            if (obj == null)
                return null;
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream())
            {
                bf.Serialize(ms, obj);
                return ms.ToArray();
            }
        }
        // Convert a byte array to an Object
        public static Object ByteArrayToObject(byte[] arrBytes)
        {
            BinaryFormatter binForm = new BinaryFormatter();
            using (MemoryStream memStream = new MemoryStream())
            {
                memStream.Write(arrBytes, 0, arrBytes.Length);
                memStream.Seek(0, SeekOrigin.Begin);
                Object obj = 
                  (Object)binForm.Deserialize(memStream);
                return obj;
            }
        }
    }
}

Ahora, con todos los servicios montados, creamos una carpeta llamada Repositories y una clase llamada RepositoryHash en la cual realizaremos las peticiones a la bbdd.

using CypherHash.Data;
using CypherHash.Helpers;
using CypherHash.Models;
using System;
using System.Collections.Generic;
using System.Linq;
namespace CypherHash.Repositories {
    public class RepositoryHash {
        CypherHashContext context;
        public RepositoryHash(CypherHashContext context) { 
                 this.context = context; 
        }
        private int GetMaxIdUser(){
            int query = 
                            this.context.Usuarios.Max(x => x.idUser) + 1;
            return query;
        }
 
        public void InsertUser
(int idEmp, string nombre, string username, string password){
            Usuario user = new Usuario();
           //user.idUser = GetMaxIdUser();
        //en el 1º registro estamos obligados a meter el valor a cañón para que no nos falle el codigo en el metodo incremental
            user.idUser = 1;
         user.name = nombre;
         user.user = username;
         String salt = CypherService.GetSalt();
         user.salt = salt;
         byte[] respuesta = CypherService.CypherHashefficent(password, salt);
         user.pswd = respuesta;
         this.context.Usuarios.Add(user);
         this.context.SaveChanges();
 }
//Comparative credentials
        public Usuario UserLogin(string UserName, string pswd)
        {
            Usuario user = 
this.context.Usuarios.Where(z => z.user == UserName).FirstOrDefault();
            if (user == null)
            {
                return null;
            }
            else
            {
                string salt = user.salt;
            byte[] Pswdbbdd = user.pswd;
          byte[] PswdTemporal = CypherService.CypherHashefficent(pswd, salt);
          bool answer = ToolKit.ArraysComparative(Pswdbbdd, PswdTemporal);
                if (answer == true)
                {
                    return user;
                }
                else
                {
                    return null;
                }
            }
        }
        public List<Usuario> GetUsuarios()
        {
            return this.context.Usuarios.ToList();
        }
    }
}
 

Con todo el entorno lógico montado, nos centramos en las vistas y controladores.

1º) Creamos la zona de registro en el controlador

public class HomeController : Controller
    {
        RepositoryHash repo;
        public HomeController(RepositoryHash repo) { this.repo = repo; } 
        public IActionResult Index()
        {
            return View();
        } 
        [HttpGet]
        public IActionResult Registrar()
        {
          return View();
        }
        [HttpPost]
        [ValidateAntiForgeryToken] 
        public IActionResult Registrar
            (String name,String user,String pswd)
        {
            this.repo.InsertUser(name, user, pswd);
            ViewData["MENSAJE"] = "Datos almacenados";
            return View();
        }
}
 
 2º) Creamos el formulario para el registro
 Registrar.cshtml 

 @model CypherHash.Models.Usuario
 
@{
    ViewData["Title"] = "Registrar";
}
 
<div class="card">
    <div class="card-header">Registrar Usuario</div>
    <div class="card-body">
        <form asp-action="Registrar">
          @Html.AntiForgeryToken() 
            <div asp-validation-summary="ModelOnly"
                  class="text-danger"></div>
            <div class="form-group">
                <label asp-for="name" class="control-label"></label>
                <input asp-for="name" class="form-control" />
                <span asp-validation-for="name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="user" class="control-label"></label>
                <input asp-for="user" class="form-control" />
                <span asp-validation-for="user" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="pswd" class="control-label"></label>
                <input asp-for="pswd" class="form-control" />
                <span asp-validation-for="pswd" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" 
                         class="btn btn-primary" />
            </div>
        </form>
    </div>
    <div class="card-footer">
        <a asp-action="Index" asp-controller="Home"
           class="btn btn-primary">
            Home
        </a>
        @if(ViewData["MENSAJE"] != null)
        {
            <p class="text-primary">@ViewData["MENSAJE"]</p>
        }
    </div>
</div>
 
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

3º) Creamos a nuestro usuario

CreateUser

Si vamos a ala base de datos y realizamos la siguiente consulta:

  • select * from CYPHERHASH

podemos observar cómo se ha insertado correctamente nuestro usuario

Ahora es momento de empezar a trabajar en la validación de las credenciales

  • El primer paso es completar nuestro controlador
using CypherHash.Models;
using CypherHash.Repositories;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
 
namespace CypherHash.Controllers
{
    public class HomeController : Controller
    {
        RepositoryHash repo;
        public HomeController(RepositoryHash repo) { this.repo = repo; }
 
        public IActionResult Index()
        {
            return View();
        }
        [HttpGet]
        public IActionResult Registrar()
        {
          return View();
        }
 
 [HttpPost]
[ValidateAntiForgeryToken] 
        public IActionResult Registrar
            (String name,String user,String pswd)
        {
            this.repo.InsertUser(name, user, pswd);
            ViewData["MENSAJE"] = "Datos almacenados";
            return View();
        }
 
        [HttpGet]
        public IActionResult Credentials()
        {
            return View();
        }
        [HttpPost]
[ValidateAntiForgeryToken] 
        public IActionResult Credentials(String username, String password)
        {
            Usuario user = this.repo.UserLogin(username, password);
            if (user == null)
            {
                ViewData["MENSAJE"] = "Usuario/Password no válidos";
            }
            else
            {
                ViewData["MENSAJE"] =
                "Credenciales correctas, Sr/Sra " + user.name;
            }
            return View();
        }
    }
}
 

Ahora nos centramos en la vista para crear el formulario de las credenciales

@model CypherHash.Models.Usuario
 
@{
    ViewData["Title"] = "Credentials";
} 
<div class="card">
    <div class="card-header">Validar Usuario</div>
    <div class="col-md-4">
        <form asp-action="Credentials">
@Html.AntiForgeryToken()
            <div asp-validation-summary="ModelOnly" 
                 class="text-danger"></div>
            <div class="form-group">
                <label asp-for="user" class="control-label"></label>
                <input asp-for="user" class="form-control" />
                <span asp-validation-for="user" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="pswd" class="control-label"></label>
                <input asp-for="pswd" class="form-control" />
                <span asp-validation-for="pswd" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" 
                         class="btn btn-primary" />
            </div>

Si metemos las credenciales mal podemos observar como nos lo indica

CredentialsOut

Como podéis observar si metemos el usuario y la contraseña correctos nos valida nuestras credenciales.

CredentialsIn

Recomendaciones

  • Utilizar autorización en los proyectos (AuthorizeAttribute, IAuthorizationFilters).
  • Estar al corriente de la capacidad de los bytes, actualmente el 512 está ya en fase beta,

lo que significa que el mínimo aumentará a 256 en vez de los actuales 128. Investigar, estar actualizado sobre ciber seguridad y el entorno donde se trabaja.

Bibliografía

https://www.genbeta.com/desarrollo/que-es-y-como-surge-la-criptografia-un-repaso-por-su-historia

https://www.thesslstore.com/blog/difference-encryption-hashing-salting

https://docs.microsoft.com/en-us/dotnet/standard/security/vulnerabilities-cbc-mode

https://cybersecurityglossary.com/hashing

https://www.genbeta.com/desarrollo/tipos-de-criptografia-simetrica-asimetrica-e-hibrida

Autor/a: Enrique. Javier. Muñoz. Romera

Curso: Desarrollo Web Full Stack, MultiCloud y Multiplataforma

Centro: Tajamar

Año académico: 2020-2021

Enlace a GitHub : https://github.com/Enrique-Munoz-Romera/CifradoHash

LinkedIn: https://www.linkedin.com/in/enrique-munoz-romera/

Leave a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.