Configuración de Jasypt para Cifrar Propiedades YAML en Spring Boot

En el desarrollo de aplicaciones modernas, garantizar la seguridad de los datos es fundamental. Almacenar credenciales y propiedades sensibles en texto plano dentro de los archivos de configuración representa un riesgo significativo. Jasypt proporciona una integración nativa con Spring Boot para cifrar y descifrar estas propiedades de manera transparente, evitando la exposición de información crítica en el código fuente.

Dependencias del Proyecto

Para integrar Jasypt en un proyecto basado en Spring Boot 3.x, se debe incluir la siguiente dependencia en el archivo pom.xml:

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>

Resolución de Conflictos en Entornos Reactivos

Al utilizar Spring Cloud Gateway (basado en WebFlux) junto con componentes que requieren Jasypt (basado en el modelo Servlet) para descifrar contraseñas de bases de datos como Redis, pueden surgir conflictos de contexto durante el inicio de la aplicación. Para mitigar este problema de incompatibilidad entre entornos reactivos y bloqueantes, es necesario forzar la carga temprana del bean de cifrado mediante la configuración de bootstrap.

Configuración del Bean de Cifrado

En lugar de pasar la contraseña maestra a través de argumentos de la línea de comandos (lo cual puede exponerla en los procesos de Docker o en el historial del sistema), se recomienda definir el bean de cifrado directamente en una clase de configuración. Esto permite gestionar la contraseña maestra a través de variables de entorno del sistema operativo.

import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SecurityEncryptionConfig {

    @Bean("customTextEncryptor")
    public StringEncryptor textEncryptor() {
        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
        SimpleStringPBEConfig config = new SimpleStringPBEConfig();
        
        // La contraseña maestra se obtiene de las variables de entorno o propiedades del sistema
        config.setPassword(System.getProperty("jasypt.encryptor.password"));
        config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
        config.setKeyObtentionIterations("100000");
        config.setPoolSize("1");
        config.setProviderName("SunJCE");
        config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
        config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
        config.setStringOutputType("base64");
        
        encryptor.setConfig(config);
        return encryptor;
    }
}

Utilidad de Cifrado y Descifrado

Se ha refactorizado la clase de utilidad para eliminar la duplicación de código en la configuración del algoritmo y utilizar la inyección de dependencias de Spring en lugar de métodos estáticos para obtener el bean del contexto.

import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.EnvironmentPBEConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class CipherManager {

    private final ApplicationContext context;
    private static final String DEFAULT_ALGORITHM = "PBEWITHHMACSHA512ANDAES_256";

    public CipherManager(ApplicationContext context) {
        this.context = context;
    }

    private EnvironmentPBEConfig buildConfig(String algorithm, String masterPassword) {
        EnvironmentPBEConfig config = new EnvironmentPBEConfig();
        config.setAlgorithm(algorithm);
        config.setPassword(masterPassword);
        config.setKeyObtentionIterations("100000");
        config.setPoolSize("1");
        config.setProviderName("SunJCE");
        config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
        config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
        return config;
    }

    public String encryptData(String plainText, String masterPassword) {
        return encryptData(plainText, DEFAULT_ALGORITHM, masterPassword);
    }

    public String encryptData(String plainText, String algorithm, String masterPassword) {
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
        encryptor.setConfig(buildConfig(algorithm, masterPassword));
        return encryptor.encrypt(plainText);
    }

    public String decryptData(String cipherText, String masterPassword) {
        return decryptData(cipherText, DEFAULT_ALGORITHM, masterPassword);
    }

    public String decryptData(String cipherText, String algorithm, String masterPassword) {
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
        encryptor.setConfig(buildConfig(algorithm, masterPassword));
        return encryptor.decrypt(cipherText);
    }

    public String encryptWithSpringBean(String plainText) {
        StringEncryptor encryptor = context.getBean("customTextEncryptor", StringEncryptor.class);
        return encryptor.encrypt(plainText);
    }

    public String decryptWithSpringBean(String cipherText) {
        StringEncryptor encryptor = context.getBean("customTextEncryptor", StringEncryptor.class);
        return encryptor.decrypt(cipherText);
    }
}

Configuración en application.yml

Para que Jasypt identifique qué propiedades deben ser descifradas automáticamente al iniciar la aplicación, los valores cifrados deben estar envueltos en la sintaxis ENC(valor_cifrado). A continuación, se muestra la configuración necesaria para vincular el bean personalizado y resolver el conflicto de carga en entornos reactivos:

spring:
  application:
    name: core-microservice
  cloud:
    bootstrap:
      # Carga anticipada para evitar conflictos en entornos reactivos (WebFlux)
      sources: com.example.security.SecurityEncryptionConfig

jasypt:
  encryptor:
    # Referencia al bean configurado manualmente
    bean: customTextEncryptor

# Ejemplo de propiedades de base de datos cifradas
datasource:
  url: jdbc:mysql://localhost:3306/mydb
  username: db_user
  password: ENC(aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890)

Etiquetas: SpringBoot Jasypt YAML SpringCloudGateway java

Publicado el 8-9 10:04