Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot: redirect from HTTP to HTTPS results in 405 error for PUT method

I have a problem very similar to this one: Redirect Post method HTTP -> HTTPS - HTTP Status 405 (Spring boot)

Basically, I'm trying to make Spring Boot serve both HTTP and HTTPS with the redirection from HTTP to HTTPS. It works, but only for GET requests. If I perform PUT request, I get "Request method 'GET' not supported" error, so looks like my PUT request is being converted to GET request somewhere somehow.

I tried both ways of configuring such redirect: define HTTPS connectivity in application.properties and then add HTTP programmatically and vise versa. Neither is working.

Here's the first approach:

@Bean
public EmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
    TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory() {
        @Override
        protected void postProcessContext(Context context) {
            SecurityConstraint securityConstraint = new SecurityConstraint();
            securityConstraint.setUserConstraint("CONFIDENTIAL");
            SecurityCollection collection = new SecurityCollection();
            collection.addPattern("/*");
            securityConstraint.addCollection(collection);
            context.addConstraint(securityConstraint);
        }
    };
    addHTTPConnector(factory);
    return factory;
}

private void addHTTPConnector(TomcatEmbeddedServletContainerFactory factory) {
    Connector connector = new Connector(TomcatEmbeddedServletContainerFactory.DEFAULT_PROTOCOL);
    connector.setScheme("http");
    connector.setPort(8080);
    connector.setRedirectPort(8443);
    connector.setSecure(false);
    Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
    protocol.setSSLEnabled(false);
    factory.addAdditionalTomcatConnectors(connector);
}

With application.properties:

server.port=8443
server.ssl.key-store=keystore.p12
server.ssl.key-store-password=password
server.ssl.keyStoreType=PKCS12
server.ssl.keyAlias=alias

Here's the second approach:

@Bean
public EmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
    TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory() {
        @Override
        protected void postProcessContext(Context context) {
            SecurityConstraint securityConstraint = new SecurityConstraint();
            securityConstraint.setUserConstraint("CONFIDENTIAL");
            SecurityCollection collection = new SecurityCollection();
            collection.addPattern("/*");
            securityConstraint.addCollection(collection);
            context.addConstraint(securityConstraint);
        }
    };
    addHTTPSConnector(factory);
    factory.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> connector.setRedirectPort(8443));
    return factory;
}

private void addHTTPSConnector(TomcatEmbeddedServletContainerFactory factory) {
    Connector connector = new Connector(TomcatEmbeddedServletContainerFactory.DEFAULT_PROTOCOL);
    connector.setScheme("https");
    connector.setPort(8443);
    connector.setSecure(true);
    Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
    protocol.setSSLEnabled(true);
    protocol.setKeystoreFile("keystore.p12");
    protocol.setKeystorePass("password");
    protocol.setKeystoreType("pkcs12");
    protocol.setKeystoreProvider("SunJSSE");
    protocol.setKeyAlias("alias");
    factory.addAdditionalTomcatConnectors(connector);
}

For both I also have

@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requiresChannel().anyRequest().requiresSecure();
    }

}

in order to make redirect work.

I have also seen this answer: Spring Boot "Request method 'GET' not supported" while redirecting POST request to https port through Catalina Connector

But I don't know what is "DEFAULT_PROTOCOL" constant. I tried adding all the methods there (POST, PUT, DELETE, GET, etc.), but it didn't help.

like image 959
Dmytro Titov Avatar asked Feb 05 '18 19:02

Dmytro Titov


People also ask

How do I fix 405 Method not allowed web API?

If you don't need to use WebDAV, then the easiest and the best way to fix "405 method not allowed" issue is to remove WebDAV from your system. You can easily get this done in "Turn Windows Features On or Off" simply un-ticking the checkbox.


1 Answers

A redirect is specifically to inform the client (e.g. web browser) to do a GET request using a given URL, so the result of a redirect cannot be a PUT, POST, DELETE, or any other HTTP method.

In this context, the main purpose of redirecting to HTTPS is to secure the connection from snooping, i.e. ensure that no one can see confidential information. This works well for a GET, since you haven't sent confidential information yet1, assuming it is the response that contains confidential information.

Redirecting a PUT or a POST to HTTPS is meaningless, since you already sent the payload (the confidential data) over an unsecure connection.

Your client needs to be told to use HTTPS before it sends the data, i.e. when it builds the PUT / POST request, it needs to be given an HTTPS URL.

Fix the client code, e.g. the JavaScript code that generates the HTTP PUT, so it uses HTTPS. Redirecting is too late, and entirely wrong.

It is actually a good thing that redirect of PUT failed, because it forces you to correctly secure your web application. If it hadn't failed, you'd mistakenly have thought that you web application was secured by the redirect, when in fact it wasn't.

1) The GET can contain confidential information too, e.g. in the query string. If it does, it should never have been sent using HTTP, so rules for securing PUT / POST also applies to GET in such cases.

like image 189
Andreas Avatar answered Nov 07 '22 20:11

Andreas