How to encrypt PHI or PII or PCI data on Response Body from HandlerInterceptor in SpringBoot ?

HandlerInterceptor gets called before the appropriate HandlerAdapter triggers the execution of the handler itself. This mechanism can be used for a large field of preprocessing aspects, e.g. for authorization checks, or common handler behavior like locale or theme changes. Its main purpose is to allow for factoring out repetitive handler code.

There are many ways to encrypt PHI or PII or PCI data but one of the easiest and global way to do is on postHandle method from HandlerInterceptor which only changes the part of the response body.

This is less invasive as we are only doing it on the response body without changing the whole codebase or at JPAs entity level. If client needs the decrypted PHI or PII data, they can be provided with the decrypt key separately. Then client can view that PHI or PII data securely on their end. Find below the code snippets to help you achieve this.

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws NoSuchFieldException, IllegalAccessException, IOException {

        //Using Reflect Utils to find the JSON based on content Field of ContentCachingResponseWrapper class.
        Field privateField = ContentCachingResponseWrapper.class.getDeclaredField("content");
        privateField.setAccessible(true);

        //Using ContentCachingResponseWrapper that caches all content written to the output stream and writer, and allows this content to be retrieved via a byte array.
        ContentCachingResponseWrapper contentCachingResponseWrapper = (ContentCachingResponseWrapper) response;

        //Returns the value of the field represented by this Field, on the contentCachingResponseWrapper object.
        FastByteArrayOutputStream fastByteArrayOutputStream = (FastByteArrayOutputStream) privateField.get(contentCachingResponseWrapper);
        InputStream inputStream = fastByteArrayOutputStream.getInputStream();

        //Get Original Response from buffered stream
        String originalJsonResponse = StringUtils.EMPTY;

        try {  //Best to use try with resource
            originalJsonResponse = new BufferedReader(
                    new InputStreamReader(inputStream, StandardCharsets.UTF_8))
                    .lines()
                    .collect(Collectors.joining("\n"));

        } catch (Exception e) {
            //Do your thingy.
        } finally {
            if (fastByteArrayOutputStream != null) {
                fastByteArrayOutputStream.close();

            }

            if (inputStream != null) {
                inputStream.close();

            }

        }


        //Now check the key of the original JSON for any PII
        if (originalJsonResponse.contains("some_PII_data_key")) {

            //ObjectMapper provides functionality for reading and writing JSON.
            ObjectMapper mapper = new ObjectMapper();
            mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
            List<Map<String, Object>> mapList = mapper.readValue(originalJsonResponse, new TypeReference<>() {
            });


            // Create a new LinkedHashMap to actually check for key and update its value
            List<LinkedHashMap<String, Object>> listOfLinkedHashMap = new ArrayList<>();

            mapList.forEach(stringObjectMap -> listOfLinkedHashMap.add((LinkedHashMap) stringObjectMap));

            listOfLinkedHashMap.forEach(linkedHashMap -> listOfLinkedHashMap.forEach(stringObjectLinkedHashMap -> stringObjectLinkedHashMap.entrySet().forEach(stringObjectEntry -> {

                //Just an example here.If key is dob or any PII update it based on your need.
                if (stringObjectEntry.getKey().equals("dob")) {
                    //Do your thing. Mask it, Encrypt it, null it etc.
                    stringObjectEntry.setValue("MASKED");
                }

            })));

            //Clears any data that exists in the buffer as well as the status code and headers. If the response has been committed, this method throws an IllegalStateException.
            response.reset();

            //Now save the updated JSON into the response writer.
            response.getWriter().write(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(listOfLinkedHashMap));
        }

    }


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *