While I agree with most of what the other answers in here have said, none of the other answers actually answered the question.
I'm going to assume that you are using SpringSession with Redis in Spring Boot.
In order to use SpringSession, you have likely configured (directly or indirectly) a servlet filter that extends SessionRepositoryFilter
.
SessionRepositoryFilter uses a SessionRepository
. Since you are using Redis, it is likely that your configuration makes use of RedisOperationsSessionRepository
.
RedisOperationsSessionRepository
implements SessionRepository
, as you might have guessed and is ultimately responsible for fetching, storing, and deleting sessions based on a key (in your case, a key that is probably stored as a cookie on the user's browser).
RedisOperationSessionRepository
, by default, uses JdkSerializationRedisSerializer
, which implements RedisSerializer
, to serialize session data prior to handing said data off to Redis.
According to the documentation for RedisOperationSessionRepository
, it is possible to set the default serializer that RedisOperationSessionRepository
will use, via it's setDefaultSerializer
method.
You could theoretically extend JdkSerializationRedisSerializer
and perform encryption and decryption there. JdkSerializationRedisSerializer
looks like this:
public class JdkSerializationRedisSerializer implements RedisSerializer<Object> {
private Converter<Object, byte[]> serializer = new SerializingConverter();
private Converter<byte[], Object> deserializer = new DeserializingConverter();
public Object deserialize(byte[] bytes) {
if (SerializationUtils.isEmpty(bytes)) {
return null;
}
try {
return deserializer.convert(bytes);
} catch (Exception ex) {
throw new SerializationException("Cannot deserialize", ex);
}
}
public byte[] serialize(Object object) {
if (object == null) {
return SerializationUtils.EMPTY_ARRAY;
}
try {
return serializer.convert(object);
} catch (Exception ex) {
throw new SerializationException("Cannot serialize", ex);
}
}
}
So a potential way to add encryption might look like:
@Component
@Qualifier("springSessionDefaultRedisSerializer") //SB 2.0.0+
public class CrypticRedisSerializer extends JdkSerializationRedisSerializer {
@Override
public Object deserialize(byte[] bytes) {
byte[] decrpyted;
try {
decrpyted = EncryptionUtils.decrypt(bytes);
return super.deserialize(decrpyted);
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// handle expections or allow to propagate, your choice!
return null;
}
@Override
public byte[] serialize(Object object) {
byte[] bytes = super.serialize(object);
try {
return EncryptionUtils.encrypt(bytes);
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// handle expections or allow to propagate, your choice!
return null;
}
}
Where EncrpytionUtils might look like:
public class EncryptionUtils {
private static SecretKeySpec skeySpec;
static {
try {
ClassPathResource res = new ClassPathResource("key.key");
if(res != null){
File file = res.getFile();
FileInputStream input = new FileInputStream(file);
byte[] in = new byte[(int)file.length()];
input.read(in);
skeySpec = new SecretKeySpec(in, "AES");
input.close();
}
}catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
}
public static byte[] encrypt(byte[] input)
throws GeneralSecurityException, NoSuchPaddingException{
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
return cipher.doFinal(input);
}
public static byte[] decrypt(byte[] input) throws GeneralSecurityException, NoSuchPaddingException{
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
return cipher.doFinal(input);
}
}
All you would need to do to implement this is make sure that you set your custom serializer as the default that RedisOperationSessionRepository
users.
Please note:
- I have not tested the above code
- I am not advocating that the above code is an ideal solution or THE solution, but simply demonstrating a mechanism for introducing encrpytion into SpringSession with Redis.
- Obviously, you can use whatever 2-way encrpytion algorithm you want. EncrpytionUtils is just an example.
- This will impact performance. How much? Hard to say without testing. Just be aware that there will be some performance impact.
- If you are really worried about encrypting session data sent to Redis, then I highly recommend that you also make sure that your servers are secured. Make sure that only the servers that need to access your Redis server can. Place it behind a firewall. If you are using a cloud service like AWS, place your Redis server in a VPN and inside of a private subnet. Check out this article.
- Redis does not support connection encryption currently. However, like they suggest, you could use Sniped to ensure your connections are encrypted.
Documentation and reference to check out:
- SpringSession
RedisOperationsSessionRepository
SessionRepository