Handling sensitive data securely is crucial in modern applications. In this article, we will build a simple Spring Boot application that securely stores and retrieves sensitive information using Redis. We will cover encryption, proper configuration, and security best practices. Spring Boot is widely used for building scalable applications, and Redis is a powerful in-memory data store that provides speed and flexibility. When dealing with sensitive data such as user credentials, API keys, or personally identifiable information (PII), we must ensure security measures are in place.

Why Use Redis?

  • Speed: Redis is an in-memory database, making data retrieval and storage extremely fast.
  • Persistence: It supports data persistence, ensuring data is not lost on restarts.
  • Encryption and Security: Redis supports encrypted connections, and we can implement additional security layers within our application.

Setting Up the Project

To start, create a Spring Boot project using Spring Initializr (https://start.spring.io/) with the following dependencies:

  • Spring Web
  • Spring Data Redis
  • Spring Boot Security
  • Spring Boot Configuration Processor

Adding Dependencies

Add the following dependencies to your pom.xml:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-crypto</artifactId>
    </dependency>
</dependencies>

Configuring Redis in Spring Boot

First, configure Redis in application.properties:

spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=your_redis_password

Configuring Redis Connection Bean

Create a Redis configuration class:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory();
    }
    
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, String> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new StringRedisSerializer());
        return template;
    }
}

Encrypting Sensitive Data Before Storing in Redis

To ensure data security, we will encrypt sensitive data before storing it in Redis. We will use Spring Security’s Encryptors.text() for encryption.

Creating an Encryption Utility Class

import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.encrypt.TextEncryptor;

public class EncryptionUtil {
    private static final String SECRET_KEY = "your_secret_key";
    private static final String SALT = "your_salt_value";

    private static final TextEncryptor encryptor = Encryptors.text(SECRET_KEY, SALT);
    
    public static String encrypt(String data) {
        return encryptor.encrypt(data);
    }
    
    public static String decrypt(String encryptedData) {
        return encryptor.decrypt(encryptedData);
    }
}

Creating a Service to Store and Retrieve Secure Data

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class SensitiveDataService {
    private final RedisTemplate<String, String> redisTemplate;

    public SensitiveDataService(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void storeSensitiveData(String key, String value) {
        String encryptedValue = EncryptionUtil.encrypt(value);
        redisTemplate.opsForValue().set(key, encryptedValue);
    }

    public String retrieveSensitiveData(String key) {
        String encryptedValue = redisTemplate.opsForValue().get(key);
        return encryptedValue != null ? EncryptionUtil.decrypt(encryptedValue) : null;
    }
}

Creating a REST Controller for Managing Sensitive Data

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/secure-data")
public class SensitiveDataController {
    private final SensitiveDataService sensitiveDataService;

    public SensitiveDataController(SensitiveDataService sensitiveDataService) {
        this.sensitiveDataService = sensitiveDataService;
    }

    @PostMapping("/store")
    public String storeData(@RequestParam String key, @RequestParam String value) {
        sensitiveDataService.storeSensitiveData(key, value);
        return "Data stored securely";
    }

    @GetMapping("/retrieve")
    public String retrieveData(@RequestParam String key) {
        return sensitiveDataService.retrieveSensitiveData(key);
    }
}

Securing the Application with Basic Authentication

Spring Security can be used to protect access to the API.

Adding Security Configuration

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .httpBasic();
    }
}

Running the Application

  1. Start the Redis server.
  2. Run the Spring Boot application.
  3. Use Postman or curl to test storing and retrieving sensitive data.

Example request to store data:

curl -X POST "http://localhost:8080/secure-data/store?key=apiKey&value=mySecret"

Example request to retrieve data:

curl -X GET "http://localhost:8080/secure-data/retrieve?key=apiKey"

Conclusion

In this article, we built a secure Spring Boot application that stores sensitive data in Redis using encryption. We covered essential security measures such as encryption, authentication, and Redis configuration. By implementing these techniques, we ensure that sensitive data remains protected from unauthorized access.

Security is an ongoing process, and additional measures such as role-based access control (RBAC), multi-factor authentication (MFA), and regular security audits should be considered for production applications. Furthermore, configuring Redis with TLS and strong authentication mechanisms will add another layer of protection.

By leveraging the power of Redis along with Spring Boot’s robust security features, we can build highly efficient, secure, and scalable applications. As threats evolve, staying updated with security best practices and continuously refining our approach to data protection will help maintain a secure and reliable system.