Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redis, SpringBoot and HttpSession: should I encrypt the session data?

I'm using Spring Boot 1.3.3 to build a web application. I use Redis for handling the session.

I'll set some "crucial" data into the HttpSession and I'd like to understand how this will work with Redis. Is the information stored server side plus a key on browser side or all the data is in a cookie in the user browser?

I'd like to see a documentation reference for the answer or to get an authoritative answer (e.g. a Pivotal dev).

like image 609
mat_boy Avatar asked Apr 07 '16 20:04

mat_boy


2 Answers

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:

  1. I have not tested the above code
  2. 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.
  3. Obviously, you can use whatever 2-way encrpytion algorithm you want. EncrpytionUtils is just an example.
  4. This will impact performance. How much? Hard to say without testing. Just be aware that there will be some performance impact.
  5. 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.
  6. 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:

  1. SpringSession
  2. RedisOperationsSessionRepository
  3. SessionRepository
like image 76
Peter Kirby Avatar answered Oct 27 '22 22:10

Peter Kirby


Very good article on redis security from the creator or redis - http://antirez.com/news/96 it's pretty interesting read. Read the comments as well.

One thing I am curious is, does your "crucial" data has to be stored in session? If it's not super critical for performance you can just save it in your DB. I use redis in our product just for storing tokens along with basic user data, I have seen people dumping large data size as a session data, which is fine but i don't think it's a really good idea.

like image 30
Puran Avatar answered Oct 27 '22 22:10

Puran