How to Set Up a Data Masking Servlet Filter

I previously demonstrated how servlet filters can be used to view and modify HTTP requests that the client sends to the web server.  This post will demonstrate how servlet filters can view and modify the HTTP responses that the web server sends back to the client.  The servlet filter that will be used in this demonstration is one to mask social security numbers (SSNs) that appear in the response messages.  Using servlet filters to perform sensitive data masking does not change the actual value of the data in the database, but it still protects the true values from being exposed through the PIA.

CLICK HERE to download the project.

Extract the zip and you should see the following files:

extract

Take the folder named “custom” and place it in the following directory:

%PS_CFG_HOME%\webserv\*your_domain_name*\applications\peoplesoft\PORTAL.war\WEB-INF\classes\com\peoplesoft\pt

customfolder

Take the folder named “DataMaskingFilter” and place it in the following directory:

 %PS_HOME%\sdk

dmffolder

Navigate to and open up the web.xml file.  The web.xml file is located in the following directory:

%PS_CFG_HOME%\webserv\*your_domain_name*\applications\peoplesoft\PORTAL.war\WEB-INF

webxml

Copy the text from the downloaded file named “webxml.txt” and paste the text into your web.xml file.  Paste in the text so that it is the first <filter> to show up in the web.xml file.  For my environment, I pasted the filter before the delivered “psfilter”.

dmfwebxml

Go to the web profile configuration page in the PIA and select the web profile that you are applying this servlet filter to.  You will need to uncheck the checkbox for the “Compress Responses” option.

webprofile

If you would like to keep the compression enabled on the web profile, then you can implement my response compression servlet filter here.

You will need to bounce the web server at this point.

After bouncing the web server, login to the PIA and navigate to a page that would normally expose an SSN value.  In my Campus Solutions environment, I navigated to the Relationships page and saw the following:

Relationships

Similarly, I got the following when I went to the Add/Update a Person Page:

AddUpdatePerson

I also tested the html output of a PS Query that returned SSNs.

selectquery

The output of the query only showed masked SSNs.

viewquery

The masking capabilities of this servlet filter can add an additional layer of security without having to modify the delivered PeopleSoft application.  This servlet filter makes use of Java regular expressions to find and mask the SSN data.  The use of regular expressions can allow for easily finding, replacing, and/or inserting certain information into the HTTP responses.  For example, one interesting use case of using regular expressions with servlet filters is for injecting JavaScript into the header tag of each html response that is sent to the client’s browser.

The demonstrated functionality of this servlet filter is powerful, but it is not very robust.  A somewhat easy way to improve the robustness would be to allow for conditionally masking data based on the user’s location and/or if the user has successfully performed 2FA for the session.  As demonstrated, the functionality is either all or nothing based on whether the filter is enabled in the web.xml file.  This provides for a rather poor user experience and can be problematic under certain circumstances.  The servlet filter can be enhanced to reference custom session variables or custom cookie values to determine if a mask is needed.

An improvement in a different direction would be to provide a separate complementary functionality. The complementary functionality would provide the ability for the masked data to be interactive with custom injected client side code that can enable the following scenarios:

  • Click To View – A user will click the masked value on a page to view the unmasked version. Under the hood, the click event will make a request to an iscript that will return the unmasked value to replace the masked value on the page.
  • Click To Challenge To View – In this scenario, A user will click the masked value on a page, but will be challenged (2FA) to view the unmasked version.  The click event will call an iscript that will return a modal window to be displayed for the user. The modal window will challenge the user for 2FA. Inputting a correct TOTP into the 2FA modal window will invoke an iscript to return the unmasked value to replace the masked value on the page.

Each of the “Click To” scenarios shall use AJAX to not lose the page’s originally loaded state. Also, the iscripts that get called on the click events will log important details of the transaction such as the emplid requesting the data, the menu/component/page names, and time.

The brilliant solution to enable the “Click To” functionality to expose the unmasked data/log the event was inspired by Grey Heller’s ERP firewall solution.  I recently had the pleasure of chatting with one of the Grey Heller representatives.  The representative had explained the importance of the ability to capture and log the details of the sensitive data exposure event. Enforcing the user to click a button to view a sensitive piece of data provides for an avenue to log the details of the specific transaction.  If you do not enforce an event like a button click to view a sensitive piece of data , then you lose the desired ability to log at a granular level.

In the future, I plan to expand on the functionality demonstrated in this post by adding the additional functionality described above.

12 thoughts on “How to Set Up a Data Masking Servlet Filter

  1. Alexei Tetenov says:

    Hi Colton,
    I was listening to your discussion on data masking filters and putting rule updates from a configuration on a page/record to the web server.
    My understanding is that the Web Profile component does this. Maybe you can examine how the Web Profile component pushes changes to the web server configuration files and use a similar methodology.
    Keep up the good work and thank you for sharing.
    -Alexei

    • Colton Fischer says:

      Hi Alexei,

      Thank you very much for listening to the podcast and commenting here.

      I definitely need to look at what the web profile component is doing to communicate config data to the web server. This would teach me the best practice for communicating information from the application to the web server.

      Meanwhile, I have come up with a way to successfully communicate information from the application to the web server, but it is not necessarily ideal. What I have done is created a JSP page on the web server that accepts an HTTP post and stores the posted data to a directory on the web server. I am able to post data to this JSP page from a PeopleSoft component with the use of Integration Broker. As I said, not ideal, but it does successfully communicate the information.

      I have found the bigger challenge to be the storage of the data on the web server and not so much the communication. The data needs to be stored in a cached state so that the filter can reference the cached data in every call to the dofilter method. Reading in the config data in the init method of the filter is not a viable option because a web server bounce would be required every time a change is made to the config data. I am currently working on creating a cache manager in Java. I will be sure to document my progress here.

      Thanks,
      Colton

  2. Jonathan Rehm says:

    Colton,

    Thanks for the excellent blog posts. They’ve inspired me to find new and interesting ways to use servlets.

    For my purposes, disabling compression isn’t an option (clients aren’t necessarily going to be on board with that) so I had to figure out how to make it work with compression enabled. I was able to do so by first decompressing the output, making my change, then recompressing it. Unfortunate waste of CPU cycles, but I was not able to figure out a way to get my manipulation to happen before the initial gzip occurs (appears to happen in `psft.pt8.util.PSHttpUtil getOutputStream`).

    So the final code looks like:

    “`
    byte[] bytes = responseWrapper.getByteArray();

    // Get content encoding
    String contentEncoding = responseWrapper.getHeader(“Content-Encoding”);
    if (contentEncoding == null) {
    contentEncoding = “”;
    }

    String html;

    // Decompress
    if (contentEncoding.equals(“gzip”)) {
    GZIPInputStream gzip = new GZIPInputStream(new ByteArrayInputStream(bytes));
    BufferedReader reader = new BufferedReader(new InputStreamReader(gzip, “UTF-8”));
    StringBuilder builder = new StringBuilder();
    String line;
    while ((line = reader.readLine()) != null) {
    builder.append(line);
    builder.append(“\n”);
    }
    html = builder.toString();
    } else {
    html = new String(bytes);
    }

    // do manipulations…

    // Compress
    if (contentEncoding.equals(“gzip”)) {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
    gzip.write(html.getBytes());
    gzip.close();

    newBytes = outputStream.toByteArray();
    } else {
    newBytes = html.getBytes();
    }

    // Update the content length and output the manipulated HTML
    res.setContentLength(newBytes.length);
    res.getOutputStream().write(newBytes);
    “`

    • Colton Fischer says:

      Hi Jonathan,

      Thanks for reading and sharing your code. This is definitely the missing piece that I’ve needed to make my response-manipulating servlet filters a viable solution. I will look into incorporating this into my filters that modify response data. Thanks again for sharing.

      UPDATE:

      I made a post here that demonstrates how to implement a custom response compression servlet filter that makes use of the code that you shared here. This filter overrides the delivered compression capabilities so that your response-manipulating servlet filters do not have to worry about decompressing responses before manipulation and re compressing after manipulation.

  3. Pratt says:

    Colton, Nice work and thank you for sharing your work with the community.

    If you possible, could you share the source code for the DataMaskingFilterClass?

    • Colton Fischer says:

      There is a clickable link at the beginning of this post (labeled “CLICK HERE”) that contains all of the source code to implement the solution that I have demonstrated in this post. This includes the source of the DataMaskingFilterClass. Please let me know if the link is not working for you and I will fix it.

      If you plan on implementing this data masking servlet filter, then I would also suggest implementing this compression servlet filter as well. If you plan implementing both of these, then make sure that you make the compression filter be the first filter specified in the web.xml’s filter chain.

      Please let me know if you have any other question.

  4. Alex says:

    Hey Colton,

    Excellent blog post. I am working on implementing similar features into our peoplesoft environments. I would like to implement the “click to view” option for the national id as well. I found your youtube video explaining the functional side of it and the logging. Could you share the code for that as well?

    Thanks,

    • Colton Fischer says:

      Hi Alex. I am glad that you are interested in implementing this type of solution. I plan on doing a post in the near future that explains and provides code samples for advanced servlet filter data masking techniques. The solution that I demonstrated in the video you are referring to has a lot of moving parts to achieve all of the functionality and I would like to write a post to thoroughly go over all of the pieces. However, the two main pieces for the “Click to View” functionality is client side script injection and encryption-based data obfuscation.

      In order for a user to be able to click a link, you must provide them the link to click (client side script injection). In order for a click event to be able to view a piece of masked data, then you must be able to reverse the mask (encryption/decryption). In a future post, I will explain how these pieces work and fit together to create a robust data masking solution.

      However, I would like to point out that the solution presented in this post cannot easily accommodate for the “Click to View” functionality. The main reason why this solution cannot accommodate for “Click to View” functionality is because we are throwing away to data as we mask it by replacing the SSN with X’s. This means that we cannot possibly revert back to the original unmasked data. Another caveat with this solution is that it is data-format dependent. The filter only discovers and masks values that look like Social Security Numbers.

      To create a better data masking servlet filter for PeopleSoft, the filter must be PeopleSoft-aware. So instead of the filter detecting data to be masked based on the format of the data, the filter needs to have the knowledge of how PeopleSoft displays data to the end-user. The filter having this knowledge is what will allow it to be able to mask any type of data and not just SSNs.

      This is probably confusing and not what you wanted to hear, but I will be able to explain this better in a future post.

Leave a Reply

Your email address will not be published.