Skip to main content

Cómo proteger los algoritmos de cifrado simétrico en Java

Escrito por
blog feature pypi spoof

18 de octubre de 2023

0 minutos de lectura

En nuestro mundo conectado, proteger los datos digitales se ha vuelto una prioridad fundamental. Dada la amplia difusión de las aplicaciones Java en distintos sectores, desde la banca hasta la atención médica, debemos destacar la importancia del cifrado. El cifrado convierte datos legibles, o texto sin formato, en datos ilegibles, o texto cifrado, para garantizar que, aunque alguien intercepte los datos cifrados, no pueda acceder a ellos sin autorización.

La primera decisión que debes tomar como desarrollador es si necesitas cifrar los datos. Aunque parezca una pregunta extraña, el principio clave del cifrado es que el texto cifrado se puede revertir al texto original. Por ejemplo, cuando hablamos de contraseñas, no queremos cifrarlas porque no queremos poder «descifrar» el texto original. Por eso, recurrimos al hashing de contraseñas en lugar del cifrado. 

Cifrado en Java

Las aplicaciones Java suelen manejar datos confidenciales, como información de clientes, datos financieros y registros de transacciones. Como desarrollador de Java, usar algoritmos de cifrado robustos es fundamental para mantener la integridad y confidencialidad de los datos sensibles. Esto protege a tus usuarios y cualquier dato esencial que tengas dentro de tu sistema.

Cifrado simétrico y asimétrico

En términos generales, podemos distinguir dos tipos de cifrado: simétrico y asimétrico. Cada tipo funciona de una manera fundamentalmente distinta y tiene diferentes casos de uso.

El cifrado simétrico usa la misma clave para cifrar y descifrar, por lo que es más rápido, pero menos seguro cuando es necesario compartir la clave. Por otro lado, el cifrado asimétrico usa un par de claves (pública y privada). La clave pública se usa para cifrar y la privada, para descifrar. Si bien el cifrado simétrico es adecuado para situaciones en las que se deben cifrar grandes volúmenes de datos, el cifrado asimétrico es ideal para compartir claves de forma segura a través de una red. Aunque distinguimos dos tipos de cifrado, en este artículo nos enfocaremos únicamente en el cifrado simétrico en aplicaciones Java.

Al usar cifrado simétrico, lo mejor es recurrir a servicios que aprovechen módulos de seguridad de hardware (HSM) para administrar claves y patrones de cifrado de sobres. Esto simplifica la administración de claves, que es la parte más difícil de una solución de cifrado. Sin embargo, si no puedes usar un servicio como Amazon Key Management Service (KMS) y aun así quieres hacerlo, la siguiente información te mostrará cómo hacerlo de forma segura. En el resto de este artículo, nos centraremos en implementar el cifrado simétrico en tu aplicación Java usando los paquetes javax.crypto.

Conoce los algoritmos de cifrado obsoletos y sus riesgos

A pesar de la importancia del cifrado, no todos los algoritmos de cifrado son iguales. Se ha descubierto que algunos algoritmos de cifrado antiguos u «obsoletos», como DES (Data Encryption Standard) o 3DES, tienen vulnerabilidades que los atacantes pueden explotar. En otro momento se consideraron seguros, pero los avances en la potencia de cómputo y las técnicas de criptoanálisis los han vuelto inseguros.

Usar algoritmos de cifrado inseguros en tus aplicaciones Java puede generar riesgos. Un atacante que acceda a tus datos cifrados podría descifrarlos si el algoritmo de cifrado está obsoleto y tiene vulnerabilidades conocidas. Esto podría provocar una filtración de datos con consecuencias potencialmente devastadoras para tus usuarios y tu organización.

Recomendaciones sobre algoritmos y modos de cifrado

En cuanto a los algoritmos de cifrado simétrico, la fundación OWASP recomienda actualmente AES (Advanced Encryption Standard) con una clave de al menos 128 bits, aunque se prefieren 256 bits. Además, debes usar el algoritmo con un modo seguro. Asimismo, los estándares y las directrices criptográficas del NIST aprueban el cifrado AES como técnica de cifrado por bloques.

Hay varios modos disponibles para cifrados como AES. Estos modos tienen distintas características de seguridad y rendimiento. En cuanto a los modos, evita ECB (Electronic Codebook), que no ofrece una confidencialidad adecuada de los mensajes. En su lugar, usa modos como CCM (Counter with CBC-MAC) o GCM (Galois/Counter Mode). Ambos son modos autenticados y garantizan la integridad, confidencialidad y autenticidad de los datos.

Cómo identificar algoritmos de cifrado débiles en Java

Es relativamente sencillo usar las API de javax.crypto para implementar un pequeño servicio de cifrado en Java. A continuación, se muestra un ejemplo de EncryptionService en Java basado en el algoritmo DES obsoleto y el modo ECB.

public class EncryptionServiceDes {

    private SecretKey secretKey;
    private Cipher cipher;

    public EncryptionServiceDes(String key) throws GeneralSecurityException {
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        DESKeySpec desKeySpec = new DESKeySpec(keyBytes);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        secretKey = keyFactory.generateSecret(desKeySpec);
        cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
    }

    public String encrypt(String original) throws GeneralSecurityException {
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        // Encrypt the original data
        byte[] encryptedData = cipher.doFinal(original.getBytes(StandardCharsets.UTF_8));
        // Encode the encrypted data in base64 for better handling
        return Base64.getEncoder().encodeToString(encryptedData);
    }
    public String decrypt(String cypher) throws GeneralSecurityException{
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        // Decode the base64-encoded ciphertext
        byte[] encryptedData = Base64.getDecoder().decode(cypher);
        // Decrypt the data
        byte[] decryptedData = cipher.doFinal(encryptedData);
        return new String(decryptedData, StandardCharsets.UTF_8);
    }
}

Ahora que incorporamos a un proyecto un algoritmo de cifrado que sabemos que es inseguro, usaremos el servicio gratuito de Snyk para detectar este uso inseguro y orientarnos hacia una mejor solución. 

Primero, crea una cuenta gratuita de Snyk. Puedes empezar fácilmente con tu cuenta de GitHub, Bitbucket, Azure AD o Docker.

Luego conecté mi repositorio de GitHub a Snyk. El análisis estático de Snyk Code me advierte de inmediato que mi aplicación usa un algoritmo criptográfico defectuoso o riesgoso. La advertencia es acertada, ya que el servicio anterior contiene varias referencias a DES. El motor de Snyk Code también me recomienda considerar el uso de AES, en consonancia con las recomendaciones de OWASP.

El análisis de seguridad del código señala DES/ECB/PKCS5Padding como un algoritmo criptográfico vulnerable, con CWE-327 y una puntuación de 828.

Aunque uso AES en el breve fragmento de código que aparece a continuación, uso el modo CBC, que no se considera seguro.

…
    public EncryptionServiceAES(String key) throws GeneralSecurityException {
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        secretKey = new SecretKeySpec(keyBytes, "AES");
        cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    }
…

Snyk también identifica el uso de modos inseguros, entre otras cosas. La siguiente captura de pantalla se tomó del plugin de Snyk, que analiza mi código dentro de IntelliJ IDEA y ofrece información útil, como ejemplos de correcciones externas.

Informe de vulnerabilidad de Snyk que muestra una advertencia sobre un algoritmo criptográfico inseguro para AES/CBC y una corrección de código que usa AES/GCM.

Cambiar EncryptionService por una versión que use AES/GCM/NoPadding, como la que se muestra a continuación, es una solución mucho mejor que la anterior y está en línea con las recomendaciones actuales de OWASP.

public class EncryptionServiceAESGCM {
    private SecretKey secretKey;
    private Cipher cipher;

    public EncryptionServiceAESGCM(String key) throws GeneralSecurityException {
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        secretKey = new SecretKeySpec(keyBytes, "AES");
        cipher = Cipher.getInstance("AES/GCM/NoPadding");
    }

    public String encrypt(String original) throws GeneralSecurityException{
        byte[] iv = new byte[16]; // Initialization Vector
        SecureRandom random = new SecureRandom();
        random.nextBytes(iv); // Generate a random IV
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(128, iv));
        byte[] encryptedData = cipher.doFinal(original.getBytes());
        var encoder = Base64.getEncoder();
        var encrypt64 = encoder.encode(encryptedData);
        var iv64 = encoder.encode(iv);
        return new String(encrypt64) + "#" + new String(iv64);
    }

    public String decrypt(String cypher) throws GeneralSecurityException{
        var split = cypher.split("#");
        var decoder = Base64.getDecoder();
        var cypherText = decoder.decode(split[0]);
        var iv = decoder.decode(split[1]);
        var paraSpec = new GCMParameterSpec(128, iv);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, paraSpec);
        byte[] decryptedData = cipher.doFinal(cypherText);
        return new String(decryptedData);
    }
}

Revisa continuamente los algoritmos de cifrado de tus aplicaciones Java

Si bien es fundamental actualizar los algoritmos de cifrado obsoletos, también es importante supervisar continuamente tu base de código para detectar nuevos problemas de seguridad. Los algoritmos de cifrado que se consideran seguros en el momento de escribir este artículo seguramente quedarán obsoletos en unos años. 

Por eso, analizar e identificar estos problemas de forma proactiva debe ser una actividad continua. Esto no se aplica únicamente a los algoritmos de cifrado, sino también, por supuesto, a otros problemas del código que mantienes. Adoptar y usar una herramienta de pruebas de seguridad de análisis estático (SAST), como Snyk Code, te ayudará a reconocer, identificar y mitigar estos problemas fácilmente antes de que causen daños.

Snyk Code para Java puede ayudarte gratis

Snyk Code es una herramienta de pruebas de seguridad de aplicaciones estáticas (SAST) de última generación que identifica vulnerabilidades de seguridad en tu base de código. Es compatible con varios lenguajes de programación, incluido Java, y analiza el código para brindar comentarios en tiempo real a los desarrolladores. Puedes integrar Snyk Code en tu canalización de CI/CD para analizar automáticamente el código con cada confirmación y detectar y corregir problemas de seguridad en las primeras etapas del ciclo de desarrollo. Además, puedes conectarlo a tu repositorio de Git, usarlo desde la línea de comandos con nuestra CLI e integrarlo con todos los IDE populares para Java. 

Así puedes detectar problemas en tu código de forma temprana y frecuente en cada etapa del ciclo de desarrollo. Y lo más importante: puedes usarlo gratis si te registras en Snyk a continuación.

Comienza con Capture the Flag

Aprende a resolver desafíos de Capture the Flag con nuestro taller virtual 101 a pedido.

Los ejemplos de código de esta publicación de blog están disponibles en GitHub para que puedas experimentar con ellos. Si quieres encontrar más recursos útiles sobre seguridad para desarrolladores de Java, consulta lo siguiente:

Leer más

blog feature snyk container custom base recommendations
Blog

Presentamos Evo Agentic AppSec: seguridad que sigue el ritmo del desarrollo con IA

El desarrollo impulsado por IA avanza más rápido que los flujos de trabajo de seguridad tradicionales. Agentic AppSec de Snyk usa agentes de seguridad para encontrar, corregir y verificar vulnerabilidades de forma continua, mientras las personas establecen el nivel.

feature insights context
Blog

Los ataques autónomos ya están aquí. La defensa debe estar a su altura.

Los atacantes autónomos están reduciendo el tiempo disponible para defenderse. Descubre cómo el descubrimiento, la corrección, la validación y la prevención continuos pueden ayudar a los equipos de seguridad a seguirles el ritmo.

illustration hero ai
Blog

El huracán de la IA ha llegado

La IA está acelerando por igual la creación de software y los ciberataques. Los líderes deben proteger los agentes y el código desde el inicio, aplicar controles en tiempo de ejecución y validar las defensas de forma independiente.