No todos los días puedes aplicar las lecciones del criptoanálisis de la Segunda Guerra Mundial a tus propias aplicaciones web. Pero hoy, eso es exactamente lo que vamos a hacer. De hecho, vamos a echar un vistazo a cómo incluso una de las herramientas criptográficas modernas más potentes puede ser derrotada por el mismo método que Alan Turing utilizó para romper la máquina Enigma -que era la herramienta criptográfica más potente de su tiempo- si los implementadores cometen los mismos errores en la transmisión de mensajes que encontró Turing.
Echemos un vistazo a una API que he evaluado recientemente, para ver cómo evitó el problema que ayudó a los Aliados a vencer a las potencias del Eje en la Segunda Guerra Mundial.
Una API de comercio electrónico: Un tarro de miel bastante dulce
El ejemplo que voy a analizar hoy es la API de una empresa de comercio electrónico para aceptar pagos. En cuanto a objetivos de ataque, no hay muchos más valiosos para los actores maliciosos que una API de pago con tarjeta de crédito. Por eso esta empresa decidió exponer al mundo una clave pública mediante la cual los consumidores de la API podían cifrar toda la información de las tarjetas de crédito que se enviaba para procesar el pago.
Así es, todos los datos enviados por este formulario serían encriptados y nunca enviados en texto plano. Esto se debe a que es importante proteger todos los datos contra ataques, no sólo el número de tarjeta. La fecha de caducidad y el CVV forman parte del paquete que hace que cualquier método de pago sea único, por lo que esta empresa lo encriptó todo. La empresa nunca enviaría nada de esto en texto plano.
Este es el aspecto de una solicitud de muestra tal y como llegó a través del cable, utilizando un proxy Charles para capturar el tráfico como un hombre en el medio:
Observa que en este ejemplo hay dos valores hash: paymentHash y cvvHash. Pensemos en los datos que contienen estos dos valores.
Para paymentHash, tenemos el número de tarjeta de crédito de 16 dígitos, encriptado utilizando la clave pública publicada para su uso en esta API. Obviamente, habrá mucha variación en los valores disponibles para el cifrado.
Para cvvHash, ¡no tenemos la misma situación en absoluto! Los CVVs son sólo de 3 dígitos. Si sólo encriptas combinaciones de tres dígitos, entonces un atacante podría construir un diccionario completo de sólo 1.000 valores diferentes (10 valores para cada uno de los 3 dígitos). Con ese diccionario, podrían encontrar el CVV de cualquier tarjeta enviada a través de esta API. Afortunadamente, mi cliente sabía cómo evitar los ataques conocidos de texto plano y ya había implementado el relleno aleatorio para evitar el mismo ataque que hizo caer a los nazis.
Descifrar el enigma
En parte, la máquina Enigma era tan difícil de descifrar porque tenía una estructura muy compleja para cifrar los mensajes y los ajustes se cambiaban con frecuencia. Sin embargo, como la máquina se utilizaba para mensajes militares, el equipo de Turing en Bletchley Park empezó a buscar el texto que cabía esperar en muchos de los mensajes.
Un tipo de mensaje que enviaban con frecuencia los militares alemanes era un informe meteorológico, y estos informes incluían la palabra alemana para "tiempo" en el mismo lugar en cada mensaje, seguida de las condiciones meteorológicas conocidas. Otro ejemplo era que muchos mensajes terminaban con "Heil Hitler" y algunos operadores utilizaban saludos estándar. Estos casos también permitieron determinar parte del texto sin formato y la configuración de cada día.
Este tipo de vulnerabilidad de cifrado es la que podría haberse producido si esta empresa de comercio electrónico no hubiera utilizado un relleno aleatorio para el CVV, ya que el número de valores posibles es muy reducido.
Evitar el mismo error
Mediante el uso de un pad aleatorio, la empresa se aseguró de que dos métodos de pago con el mismo CVV no revelaran nada sobre lo que se estaba transmitiendo. He aquí algunos ejemplos en los que el mismo valor de "777" podría encriptarse para tener valores diferentes mediante el uso de un pad:
$ echo -n 777 | openssl rsautl -inkey public_key.pem -pubin -pkcs |
Como puedes ver, los comandos ejecutados arriba son todos iguales, pero debido a los estándares en la encriptación RSA, los valores de salida son todos completamente diferentes.
No basta con buenos algoritmos de cifrado
Este ejemplo debería ayudarte a comprender que no basta con poner en marcha un buen esquema de cifrado. Incluso con algoritmos de cifrado muy sofisticados, si se utiliza la misma entrada una y otra vez, es posible que los datos que se intentan transmitir de forma segura queden al descubierto. Es muy importante -especialmente cuando se trata de información de pago- asegurarse de que no es fácil adivinar los valores que se envían a través del cable.
Otro ejemplo en el que el relleno es importante es el almacenamiento de contraseñas. Los desarrolladores saben que necesitan utilizar una buena función hash unidireccional para asegurarse de que ni siquiera ellos puedan leer las contraseñas que envían sus usuarios. Para ello, los desarrolladores deben añadir una cadena aleatoria (denominada salt ) a cualquier contraseña antes de aplicar el hash.
Conclusión
Integrar la seguridad en sus aplicaciones web es una tarea de enorme importancia. Asegurarse de que los datos confidenciales de sus usuarios no se descifran fácilmente genera confianza en los clientes y garantiza la longevidad de su negocio. Al fin y al cabo, nadie quiere que los medios de comunicación se enteren de que su sitio ha filtrado información de pago de sus clientes. Si eres capaz de asegurarte de que no encriptas exactamente los mismos valores una y otra vez, evitarás un error muy básico y además mantendrás a salvo los datos de tus clientes.
Para obtener más información sobre las mejores prácticas de seguridad, consulte el amplio conjunto de guías de seguridad de Linode.
Comentarios