Integridad y confidencialidad: Porque no entregar el hash de un número de tarjeta
Tags: cybersecurity, ciberseguridad, hash, it
Published: 2024-05-02 02:34:26

## Escenario
Usted es responsable de la ciberseguridad en su organización, y se presenta una situación en la cual proponen como medida de seguridad, entregar el hash de la tarjeta en vez del número de tarjeta en las transacciones que van hacia la aplicación móvil de su empresa. Argumentan que el hash es una operación no reversible, por lo cual, un atacante no podría obtener el dato original. Sin embargo, usted aún tiene la pregunta: ¿Es posible determinar el número de tarjeta a partir del hash?.

## Algoritmos para la generación de hash
Los algoritmos de generación de hash son funciones criptográficas diseñadas para convertir datos de cualquier tamaño en un valor fijo, estos valores son únicos para cada entrada única (en realidad la cantidad de combinaciones es muy grande, pero finita, ver [colisión de hash](https://es.wikipedia.org/wiki/Colisi%C3%B3n_%28hash%29)), es decir, pequeñas diferencias en los datos de entrada producirán hashes completamente diferentes. Algunos de los algoritmos más utilizados son:

### MD5 (Message Digest Algorithm 5)
MD5 divide los datos en bloques de 512 bits y procesa cada bloque en un ciclo de 64 pasos, utiliza cuatro variables de 32 bits, y el resultado es un hash de 128 bits. Aunque se usa ampliamente para verificar la integridad de los datos, no se recomienda su aplicación en seguridad debido a vulnerabilidades conocidas.

### SHA-1 (Secure Hash Algorithm 1)
SHA-1 procesa bloques de 512 bits y utiliza un ciclo de 80 pasos, resultando en un hash de 160 bits; fue ampliamente utilizado en protocolos de seguridad como TLS y SSL, pero ha sido reemplazado por versiones más seguras de SHA debido a debilidades en su estructura.

### SHA-3 (parte de la familia SHA-2)
SHA-3 difiere significativamente en su enfoque usando la función de permutación Keccak, procesando los datos en una matriz de estado de 1600 bits y permite salidas de hash de diferentes tamaños (como 224, 256, 384, 512 bits).

### SHA-256
SHA-256 procesa bloques de datos de 512 bits utilizando un ciclo de 64 pasos y produce un hash de 256 bits, es ampliamente utilizado en aplicaciones de blockchain y criptomonedas. 

### Blake2
Es una versión mejorada del algoritmo Blake, procesa bloques de 512 o 1024 bits y es más rápido que MD5, SHA-1 y SHA-256 en software. Ofrece alta velocidad y seguridad, es utilizado en contextos donde se requieren ambas, como en bases de datos y autenticación de software.

### Whirlpool
Utiliza una matriz de estado de 512 bits y realiza un total de 10 rondas de transformaciones, el resultado es un hash de 512 bits. Es menos común que SHA o MD5, se aplica en seguridad para garantizar la integridad de los datos.

## Estructura del número de tarjeta de crédito
La estructura de un número de tarjeta de crédito está estandarizada, normalmente, un número de tarjeta de crédito consta de 16 dígitos, aunque algunos sistemas como American Express usan 15 dígitos. Estos dígitos están estructurados de la siguiente manera:

- Número de Identificación del Emisor (IIN): Los primeros 6 dígitos del número de la tarjeta identifican a la institución financiera que emitió la tarjeta. Los primeros 2 dígitos indican el tipo de tarjeta, como Visa, MasterCard, American Express, etc. De los dígitos 3 a 6 se cuenta con la identificación específica del banco o institución financiera que emite la tarjeta (BIN).
- Número de cuenta del titular: Los dígitos siguientes hasta el penúltimo son el número de cuenta único del titular de la tarjeta, este número es asignado por la institución financiera.
- Dígito de verificación: El último dígito de la tarjeta es un dígito de verificación o dígito de control, calculado a través del algoritmo de Luhn, y permite validar la autenticidad del número de tarjeta de crédito.  El proceso involucra sumar y manipular los dígitos de ciertas maneras para producir un solo dígito que debe coincidir con este último número en la tarjeta.

### Bases de datos de números de identificación bancaria
El número de identificación bancaria (BIN), es utilizado en el sistema de tarjetas (crédito/débito) para identificar la institución que emitió la tarjeta al titular y sus características. Existen bases de datos que se utilizan para identificar rápidamente la institución que emitió una tarjeta de crédito o débito a partir de la secuencia de dígitos 3 a 6. En la red podemos encontrar algunos sitios que permiten acceder a una lista de BIN, como se muestra en las siguientes imágenes.

En bincheck.io se encuentran BIN de 90 bancos en Colombia (según el sitio):
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5iwbcl47eza9pj99htoe.png)

En iinbinlist.com se presenta algo similar:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ujh0ywb2jlhhs00nrmdl.png)

Igualmente en bankbinlist.com:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tqtk46mst3peerarj83a.png)

Con las consultas de BIN mostradas anteriormente, se quiere evidenciar que estos son datos que pueden ser conocidos fácilmente por un atacante, sin embargo, también es necesario tener en cuenta que son una parte fundamental del ecosistema de pagos electrónicos, facilitando la comunicación y la seguridad entre las instituciones financieras, comerciantes y consumidores.

## Respondiendo la pregunta
Después de la teoría, vuelve la pregunta: ¿Es posible determinar el número de tarjeta a partir del hash?. Supongamos que un atacante ha obtenido los datos de la transacción y esta contiene el hash sha-256 del número de tarjeta. Sabemos ya, que el IIN (los primeros 6 dígitos) dependen de la franquicia, el banco y segmento de clientes, que pueden ser conocidos, ya sea a través de alguna base de datos o adquiriendo un producto de la entidad a atacar. También, el último digito (de verificación) pueden ser calculado utilizando el algoritmo de Luhn. Es decir que de los 16 dígitos, tiene que averiguar 9 dígitos para obtener el número correspondiente al hash de la tarjeta, el atacante no tiene otra pista, y procede a utilizar fuerza bruta.

Para los 9 dígitos del número de cuenta (posiciones 7 a 15), hay 10<sup>9</sup> combinaciones posibles (desde 000000000 hasta 999999999). Se procede a generar el siguiente código en python que generará los hash de los números de tarjeta de manera secuencial, tomando un IIN encontrado en las bases de datos (491511), esto a manera de ejemplo. Para ello se definen tres funciones, una que permite calcular el digito de verificación de acuerdo al algoritmo de luhn, otra que genera las combinaciones posibles a partir de un IIN fijo, y finalmente la que calcula el hash sha-256 de cada potencial número de tarjeta. En la función main se genera un archivo csv por cada 10 millones de hash generados (correspondientes al 1% del procesamiento).

```
import hashlib
import time
import csv

def calcular_digito_luhn(numero):
    digitos = [int(d) for d in str(numero)][::-1]
    suma_total = 0
    for i, digito in enumerate(digitos):
        if i % 2 == 1:
            digito *= 2
            if digito > 9:
                digito -= 9
        suma_total += digito
    return (10 - (suma_total % 10)) % 10

def generar_combinaciones(numero_fijo):
    numero_fijo = str(numero_fijo)
    # Rango de números para generar desde 0 hasta 999999999
    for num in range(1000000000):
        # Convertir el número a cadena y rellenar con ceros para tener siempre 9 dígitos
        numero_cliente = str(num).zfill(9)
        numero = numero_fijo + numero_cliente
        digito_luhn = calcular_digito_luhn(numero)
        numero_completo = numero + str(digito_luhn)
        yield numero_completo

def aplicar_sha256(numero):
    hash_obj = hashlib.sha256()
    hash_obj.update(numero.encode())
    return hash_obj.hexdigest()

def main():
    start_time = time.time()
    combinaciones = generar_combinaciones(491511)
    count = 0
    batch_num = 1

    # Crear el archivo CSV inicial
    while True:
        with open(f'hashes_batch_{batch_num}.csv', 'w', newline='') as file:
            writer = csv.writer(file)
            writer.writerow(['Número', 'Hash SHA-256'])
            for _ in range(10000000):
                try:
                    numero = next(combinaciones)
                except StopIteration:
                    break
                hash_result = aplicar_sha256(numero)
                writer.writerow([numero, hash_result])
                count += 1
            
            # Comprobación de finalización
            if count % 10000000 != 0 or count == 0:
                break

        print(f"Archivo {batch_num} completo con 10,000,000 hashes. Tiempo parcial: {time.time() - start_time:.2f} segundos")
        batch_num += 1

    end_time = time.time()
    print("Proceso completo.")
    print("Tiempo total de ejecución: {:.2f} segundos".format(end_time - start_time))

if __name__ == "__main__":
    main()
```
Hay que mencionar que el código desarrollado es sólo una aproximación, dado que se puede hacer más eficiente aplicando procesamiento en paralelo, realizando operaciones de escritura en disco de manera menos frecuente, reduciendo la llamadas a funciones, entre otras estrategias. Adicionalmente, el código será ejecutado en un equipo de computo de uso común.

A continuación se observa la captura de la ejecución realizada para los primeros 200 millones de combinaciones, suficiente para obtener los datos requeridos.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k0t31wpo0rcz772vxd0c.png)

A modo de ejemplo, se muestra el resultado parcial del último archivo generado:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/11frjx6tk6f2v75j5jz8.png)

En la siguiente imagen, se observa el tiempo que toma generar cada archivo de 10 millones de hashes:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p60cwmxtaap5nswle6fp.png)

De acuerdo a los datos mostrados, en promedio la generación de un archivo con 10 millones de números y su correspondiente hash toma 79.47 segundos. Tomando como base que cada archivo requiera de 80 segundos de procesamiento, en total se requieren alrededor de 2 horas 15 minutos para completar todo el procesamiento, lo cual desde el punto de vista de un atacante es asumible. Cada banco tiene varios IIN, el esfuerzo que implica calcular los hashes para cada uno de estos tampoco sería un limitante.

## Conclusiones

Como se pudo observar, entregar el hash de un número de tarjeta no constituye una medida de seguridad, a pesar que la operación para su cálculo no es reversible, dado que no impide conocer el dato original cuando se tienen pistas sobre este. 
Se debe tener en cuenta, que de los pilares de la seguridad de la información, las operaciones hash están diseñadas para evaluar la integridad de los datos, no la confidencialidad. Aplicar los conocimientos y herramientas de manera adecuada, es el primer paso para una operación segura, no sólo en el mundo cibernético.