Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does my Java servlet filter not work over HTTPS?

Spring (Boot) here, although that shouldn't matter at all. I am trying to learn more about how HTTP/S proxies work and am building one to run locally on my machine. I wrote (and registered) a servlet filter that replaces the body of the HTTP response with a silly HTML message:

public class DummyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse httpServletResponse = (HttpServletResponse)response;

        String html = "<html><head><title>Awesome!</title></head><body>Proxy is working!</body></html>";

        httpServletResponse.writer.write(html);
        httpServletResponse.writer.flush();

        return;
    }

    @Override
    public void destroy() {
    }
}

I then run my Spring app, and change my browser's proxy settings to point to my app (localhost:8080).

I am now able to go to HTTP websites and see my dummy message ("Proxy is working!") as the HTML output. Success!!! However, I then went to Google's homepage, which apparently uses HTTPS, and the Google homepage rendered just fine.

So I changed my browser to also use my Spring app for proxying SSL (again, localhost:8080) and tried again. This time when I went to Google, my browser gave me an error stating that there was a problem with the connection. I assume this is because my stupid-simple proxy is causing problems with the SSL 'handshake' between the browser and the site requiring SSL (in this case, Google).

I know that using proxies over SSL is certainly possible, because (at the very least) the Charles Proxy can be configured to do this. Apparently, Charles operates by dynamically generating a cert for the site you are trying to access, based off its own root CA cert. Charles and the SSL site use the site's cert, and the communication between the browser and Charles uses Charles' cert.

But knowing that doesn't help me understand why my simple proxy is causing problems in SSL-land in the first place. What would I need to change in my code so that it could behave the same with HTTPS as it does with HTTP?


Update

I'm wondering if the following would work for me:

  1. Create a self-signed wildcard cert for, say *.example.com (anything dot com)
  2. Configure my Sring app to use this wildcart cert and to serve HTTPS from port 443 (HTTPS default)
  3. Configure the browser's SSL proxy settings to point to localhost:443
  4. Add my self-signed wildcard cert to my browser's trust store
  5. Now when I go to any URL under https://example.com, the browser reaches out to my proxy, which serves it back the self-signed cert (which it now trusts), and the proxy can talk with the example.com site's actual cert just fine.

Would this, or something similar, fix my problem?

like image 717
smeeb Avatar asked Jul 06 '16 09:07

smeeb


3 Answers

The problems indicated really are HTTP/S basic ones.

When directing your browser to use a proxy at the given address (localhost:8080), the browser is causing any subsequent HTTP call to the configured "proxy server" indicating to this "proxy" that it should execute a call on behalf of the calling browser to the original URL.

In your case the "proxy" really is returning a canned message and does not really try connecting to the original URL. (At least you did not tell anything about what your "Proxy Server" is going to do in order to contacting the originally targeted site.) That would be the more material aspect of a proxy basic functionality.

In the case of connecting to a server using HTTPS, it is then important, how you did configured that proxy connection with your browser.

It would be possible to use a plain HTTP connection to the proxy and still request the proxy to using a HTTPS connection for the "external" call. (Such configuration, however, is not so much widespread, as the proxy need to e.g. carefully treat redirects. Also it would invalidate some gains of using HTTPS in the first place (at least on the communication segment from browser to proxy.)

Most likely your browser configuration used an HTTPS connection to the "proxy" (localhost:8080). Then the browser tried an HTTPS request and hit an error as the target "responded" with regular HTTP.

Configuring your proxy servlet to accepting HTTPS calls would "repair" that problem. (Form this, your steps from the "Update" edit will "solve" the errors.) However, you do not need to use port 443 at localhost. Any port will do. Just if you want to provide an HTTP and an HTTPS proxy at the same time do you need to allocate two ports (e.g. you could use 8080 for HTTP and 8081 for HTTPS).

Just to empachase: Seeing the "Proxy is working" message does not prove having a working proxy. It just proves that your browser does talk to your servlet. (As you do not read any header information, it is not different from just calling URL localhost:8080 directly.)

Beyond getting "contacted" a working proxy would need to take the request, analyze the headers passed in and react according to the standards (especially executing the requested "external" call and returning the results). (Of course, you have read RFCs related to HTTP protocol (e.g. RFC7230)?)

like image 124
rpy Avatar answered Nov 13 '22 10:11

rpy


You must create a HTTPS connector. In order to create an HTTPS connector, you will need a few things; but most importantly, you will need to generate Certificate keystore that is used to encrypt and decrypt the SSL communication with the browser.

If you are using Unix or Mac, you can do it by running the following command: $JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA On Windows, this could be achieved via the following code: "%JAVA_HOME%\bin\keytool" -genkey -alias tomcat -keyalg RSA

During the creation of the keystore, you should enter the information that is appropriate to you, including passwords, name, and so on. For the purpose of Once the execution is complete, a newly generated keystore file will appear in your home directory under the name: .keystore. Note You can find more information about preparing the certificate keystore at tomcat.

With the keystore creation complete, you will need to create a separate properties file in order to store your configuration for the HTTPS connector, such as port and others. After that, you will create a configuration property binding object and use it to configure our new connector.

See this example of a prop file. you can cll it whatever name you want : tomcat.https.properties

custom.tomcat.https.port=8443
custom.tomcat.https.secure=true
custom.tomcat.https.scheme=https
custom.tomcat.https.ssl=true
custom.tomcat.https.keystore=${user.home}/.keystore
custom.tomcat.https.keystore-password=changeit
like image 38
AchillesVan Avatar answered Nov 13 '22 11:11

AchillesVan


Don't need to change your code.You might be missing configuration.Check whether you configured ssl correctly . Follow the steps to enable ssl in your spring app:

  1. create Keystore file

    keytool -genkey -alias tomcat -keyalg RSA

2.Configuring Tomcat for using the keystore file – SSL config in your server.xml

Find the following declaration:

<!--
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
    maxThreads="150" scheme="https" secure="true"
    clientAuth="false" sslProtocol="TLS" />
-->

Uncomment it and modify it to look like the following:

Connector SSLEnabled="true" acceptCount="100" clientAuth="false"
    disableUploadTimeout="true" enableLookups="false" maxThreads="25"
    port="8443" keystoreFile="/Users/prashant/.keystore" keystorePass="password"
    protocol="org.apache.coyote.http11.Http11NioProtocol" scheme="https"
    secure="true" sslProtocol="TLS" />

3.Configuring your app to work with SSL (access through https://localhost:8443/yourApp)

add in your web.xml file of your application.

<security-constraint>
    <web-resource-collection>
        <web-resource-name>securedapp</web-resource-name>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

It works for me with my custom filter.

Hope so it will help you..

like image 37
Prashant Thorat Avatar answered Nov 13 '22 11:11

Prashant Thorat