Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DefaultRedirectStrategy includes firewalled semicolon jsessionid in url #15077

Open
xenoterracide opened this issue May 15, 2024 · 2 comments
Open
Assignees
Labels
in: web An issue in web modules (web, webmvc) status: feedback-reminder We've sent a reminder that we need additional information before we can continue status: waiting-for-feedback We need additional information before we can continue

Comments

@xenoterracide
Copy link

xenoterracide commented May 15, 2024

according to #5267 I would think that a redirect with the jsessionid in the url shouldn't happen; as that says it's disabled by default.

DEBUG 3733950 - c.xeno.test.auth.serv.AuthorizationServerTest                : Running with Spring Boot v3.2.5, Spring v6.1.6
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "POST /login HTTP/1.1[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "Content-Type: application/x-www-form-urlencoded;charset=UTF-8[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "Accept-Encoding: gzip, x-gzip, deflate[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "Content-Length: 27[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "Host: localhost:34983[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "Connection: keep-alive[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "User-Agent: Apache-HttpClient/5.2.3 (Java/21.0.2)[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "username=user&password=pass"
DEBUG 3733950 - o.spri.secu.web.DefaultRedirectStrategy                      : Redirecting to http://localhost:34983/login;jsessionid=751E2839A9F66450AF99742B555A39F2
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "HTTP/1.1 302 [\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "Vary: Origin[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "Vary: Access-Control-Request-Method[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "Vary: Access-Control-Request-Headers[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "Set-Cookie: JSESSIONID=751E2839A9F66450AF99742B555A39F2; Path=/; HttpOnly[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "X-Content-Type-Options: nosniff[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "X-XSS-Protection: 0[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "Cache-Control: no-cache, no-store, max-age=0, must-revalidate[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "Pragma: no-cache[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "Expires: 0[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "X-Frame-Options: DENY[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "Location: http://localhost:34983/login;jsessionid=751E2839A9F66450AF99742B555A39F2[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "Content-Length: 0[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "Date: Wed, 15 May 2024 17:46:10 GMT[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "Keep-Alive: timeout=60[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "Connection: keep-alive[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 << "[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "GET /login;jsessionid=751E2839A9F66450AF99742B555A39F2 HTTP/1.1[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "Accept-Encoding: gzip, x-gzip, deflate[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "Host: localhost:34983[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "Connection: keep-alive[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "User-Agent: Apache-HttpClient/5.2.3 (Java/21.0.2)[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "Cookie: JSESSIONID=751E2839A9F66450AF99742B555A39F2[\r][\n]"
DEBUG 3733950 - o.apac.hc.clie.http.wire                                     : http-outgoing-0 >> "[\r][\n]"
DEBUG 3733950 - o.spri.secu.web.fire.HttpStatusRequestRejectedHandler        : Rejecting request due to: The request was rejected because the URL contained a potentially malicious String ";"
package com.xenoterracide.test.authorization.server;

import com.xenoterracide.tools.java.annotation.ExcludeFromGeneratedCoverageReport;
import java.util.UUID;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@SpringBootApplication(proxyBeanMethods = false)
public class AuthorizationServer {

  public static final String CLIENT_ID = "client";
  public static final String REDIRECT_URI = "http://localhost:3000";

  AuthorizationServer() {}

  @ExcludeFromGeneratedCoverageReport
  public static void main(String[] args) {
    SpringApplication.run(AuthorizationServer.class, args);
  }

  @Bean
  @Order(1)
  public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
    http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
    http
      // Redirect to the login page when not authenticated from the
      // authorization endpoint
      .exceptionHandling(
        exceptions ->
          exceptions.defaultAuthenticationEntryPointFor(
            new LoginUrlAuthenticationEntryPoint("/login"),
            new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
          )
      )
      // Accept access tokens for User Info and/or Client Registration
      .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));

    return http.cors(Customizer.withDefaults()).build();
  }

  @Bean
  @Order(2)
  public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    http
      .authorizeHttpRequests(authorize -> authorize.requestMatchers("/oauth/authorize").permitAll())
      .authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
      // Form login handles the redirect to the login page from the
      // authorization server filter chain
      .formLogin(Customizer.withDefaults());

    return http.cors(Customizer.withDefaults()).build();
  }

  @Bean
  public CorsConfigurationSource corsConfigurationSource() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    config.addAllowedOrigin(REDIRECT_URI);
    config.setAllowCredentials(true);
    source.registerCorsConfiguration("/**", config);
    return source;
  }

  @Bean
  RegisteredClientRepository registeredClientRepository() {
    var publicClient = RegisteredClient.withId(UUID.randomUUID().toString())
      .clientId(CLIENT_ID)
      .clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
      .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
      .redirectUri(REDIRECT_URI)
      .scope(OidcScopes.OPENID)
      .scope(OidcScopes.PROFILE)
      .scope(OidcScopes.EMAIL)
      .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).requireProofKey(true).build())
      .build();

    return new InMemoryRegisteredClientRepository(publicClient);
  }
}
package com.xenoterracide.test.authorization.server;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.HttpComponentsClientHttpConnector;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.reactive.server.WebTestClientConfigurer;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.client.RestClient;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;

@ActiveProfiles({ "test", "test-http" })
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class AuthorizationServerTest {

  @LocalServerPort
  int port;

  @SuppressWarnings("NullAway")
  @Value("${spring.security.user.name}")
  String user;

  @SuppressWarnings("NullAway")
  @Value("${spring.security.user.password}")
  String pass;

  @SuppressWarnings("NullAway")
  @Value("${spring.security.oauth2.authorizationserver.endpoint.authorization-uri}")
  String authorizationUriPath;

  @SuppressWarnings("NullAway")
  @Value("${spring.security.oauth2.authorizationserver.endpoint.token-uri}")
  String tokenUriPath;

  String client = "client";

  @Test
  void authn() {
    var restClient = RestClient.builder()
      .requestFactory(new HttpComponentsClientHttpRequestFactory())
      .baseUrl("http://localhost:" + this.port)
      .messageConverters(converters -> {
        converters.addFirst(new OAuth2AccessTokenResponseHttpMessageConverter());
      })
      .build();

    var credentials = new LinkedMultiValueMap<String, String>();
    credentials.add("username", this.user);
    credentials.add("password", this.pass);

    var login = restClient
      .post()
      .uri("/login")
      .contentType(MediaType.APPLICATION_FORM_URLENCODED)
      .body(credentials)
      .retrieve()
      .toEntity(String.class);
@xenoterracide xenoterracide added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels May 15, 2024
@xenoterracide xenoterracide changed the title Spring security redirecting to include jsessionid in url Spring security DefaultRedirectStrategy to include semicolon jsessionid in url May 15, 2024
@xenoterracide xenoterracide changed the title Spring security DefaultRedirectStrategy to include semicolon jsessionid in url DefaultRedirectStrategy to include semicolon jsessionid in url May 15, 2024
@xenoterracide xenoterracide changed the title DefaultRedirectStrategy to include semicolon jsessionid in url DefaultRedirectStrategy includes firewalled semicolon jsessionid in url May 15, 2024
@jzheaux jzheaux self-assigned this May 21, 2024
@jzheaux jzheaux added in: web An issue in web modules (web, webmvc) and removed status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels May 21, 2024
@jzheaux
Copy link
Contributor

jzheaux commented May 24, 2024

Thanks for the report, @xenoterracide. You are correct that it is disabled by default.

I can't see yet from the code you've shared how the jsessionid is appearing in the URL. Nor am I familiar with the redirect pattern where posting to /login results in a redirect back to /login (other than maybe you are testing a failed login?).

Can you please provide a minimal sample that reproduces the issue?

@jzheaux jzheaux added the status: waiting-for-feedback We need additional information before we can continue label May 24, 2024
@spring-projects-issues
Copy link

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

@spring-projects-issues spring-projects-issues added the status: feedback-reminder We've sent a reminder that we need additional information before we can continue label May 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web An issue in web modules (web, webmvc) status: feedback-reminder We've sent a reminder that we need additional information before we can continue status: waiting-for-feedback We need additional information before we can continue
Projects
Status: No status
Development

No branches or pull requests

3 participants