0

I have a Java web application that's hosted in Amazon Web Services and it's running on the retired Tomcat 8 with Amazon Linux.

I've attempted to create a new environment to test running the app on the latest Tomcat 8.5 with Corretto 11 running on Amazon Linux 2.

During this process I hit a big problem, within my app it's using a base URL, e.g. https://myserver.com and it's doing some string concatenation to add the path e.g. /rest/myendpoint which of course should be resolved as

https://myserver.com/rest/myendpoint

But unfortunately unknowingly it's being passed through C#'s new URL() which is appending a trailing slash before the concatenation.

So

https://myserver.com/rest/myendpoint

gets sent to the server as

https://myserver.com/rest//myendpoint

(Note the rest// should be rest/)

After deploying the Java war file to the new environment some requests from the app fail with a 404. I've analysed these network logs and of course it's all the URLs that have a double-escape in the path.

As a test in Postman writing the request to have a single slash worked fine, double-quotes failed with a HTTP 404, just like the app.

The problem now is that the app is already used in the wild, we can't change the app because this will involve a long delay (getting it built, app store approval process, then waiting for everyone to upgrade the app), most likely months.

I can't find anything in the AWS portal to configure this.

I did try swapping to Nginx but I experienced the same error.

I did see some people mentioning using .eb-extensions, but the whole apache re-write / mod rules are very foreign to me. In effect I want to re-write any double-slashes after http:// or https:// to single-slashes. Can you please advise on how this could be done?

I'm using the Elastic Beanstalk with Tomcat (currently backed with Apache) with the latest Tomcat Platform version - Tomcat 8.5 with Corretto 11 running on 64bit Amazon Linux 2.

Thank you.

Olaf Kock
  • 46,930
  • 8
  • 59
  • 90
RobbiewOnline
  • 1,350
  • 1
  • 16
  • 36
  • Is it Java, or is it C# that's generating the URL? – Olaf Kock Mar 06 '21 at 07:30
  • Hi @OlafKock the app is c# but unfortunately I can’t wait for the app to be fixed, rolled out and a majority of the users to update. It’s the server-side that will need some sort of Apache re-write wrapped in AWS .ebexensions to do this cleaning task in the meantime. – RobbiewOnline Mar 06 '21 at 09:59
  • https://stackoverflow.com/questions/17080652/issue-in-removing-double-or-more-slashes-from-url-by-htaccess should give you the necessary RewriteRules, and https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/java-tomcat-proxy.html has example where to place the file. – Pasi Mar 15 '21 at 17:52

1 Answers1

0

Because I didn't really want to faff around with the .ebextensions, I extended an existing servlet filter (the earliest in the chain) to check the path. I was then able to forward using the dispatcher and it works perfectly.

Whilst I'm sure that I can do something cleaner with reg-ex this done the trick for me and I could test it locally with Postman first.

package com.devology.servlet.filters;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class AuthenticationFilter implements Filter {

    public AuthenticationFilter() {
    }

    private boolean doBeforeProcessing(ServletRequest request, ServletResponse response)
            throws IOException, ServletException {

        HttpServletRequest hsr = (HttpServletRequest) request;
        final String path = hsr.getServletPath();
        String fullUrl = hsr.getRequestURL().toString();

        String remoteAddress = hsr.getRemoteAddr();

        // Example URL from bad app...
        //
        // http://localhost:8084/MyApp//rest/login
        //
        // path        =                                    /rest
        // fullUrl     = http://localhost:8084/MyApp//rest/login
        // indexOfPath =                                    ^ = 34
        // 
        // If the character before the start of the path is also a slash then
        // we need to internally forward
        int indexOfPath = fullUrl.indexOf(path + "/");
        boolean needsUrlRewrite = false;
        if (indexOfPath != -1) {
            String previousCharacter = fullUrl.substring(indexOfPath - 1, indexOfPath);
            needsUrlRewrite = "/".equals(previousCharacter);
        }
        if (needsUrlRewrite) {

            // Just take the path from the point of indexOfPath, .e.g /rest/login
            String rewrittenPath = fullUrl.substring(indexOfPath);
            RequestDispatcher dispatcher = request.getServletContext().getRequestDispatcher(rewrittenPath);
            dispatcher.forward(request, response);
            return false;
        }

        // Any other checks like authorisation, or return true to let all through
        return true;
    }

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain)
            throws IOException, ServletException {

        if (doBeforeProcessing(request, response)) {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void init(FilterConfig fc) throws ServletException {
        // Your Init
    }

    @Override
    public void destroy() {
        // Your cleanup
    }

}

This assumes that you have it configured in web.xml like this...

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
    <filter>
        <filter-name>AuthenticationFilter</filter-name>
        <filter-class>com.devology.servlet.filters.AuthenticationFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AuthenticationFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspf</url-pattern>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>10</session-timeout>
    </session-config>
    <error-page>
        <error-code>500</error-code>
        <location>/error-500.html</location>
    </error-page>
    <error-page>
        <error-code>404</error-code>
        <location>/error-404.html</location>
    </error-page>
</web-app>

I didn't fix the issue with the .ebextensions and Apache configuration because I didn't really know what I was doing and the above servelet filter done exactly what I needed.

Dharman
  • 30,962
  • 25
  • 85
  • 135
RobbiewOnline
  • 1,350
  • 1
  • 16
  • 36