[JAVA] JSESSIONID n'a pas pu être attribué à l'URL lors de l'utilisation de Spring Security

Une erreur se produit désormais lors de la mise à niveau de Spring Security dans une application qui inclut le JSESSIONID dans l'URL. J'ai enquêté sur la cause, donc je vais la résumer.

Eh bien, pourquoi ne pas inclure JSESSIONID dans l'URL maintenant?

environnement

Reproduire l'événement

Une certaine préparation est nécessaire pour reproduire l'événement.

Gérer JSESSIONID par URL

Dans le cas d'un navigateur pouvant utiliser des cookies, JSESSIONID est géré à l'aide de cookies. Modifiez les paramètres du conteneur de servlet pour forcer la gestion par URL.

Lors de l'utilisation de Spring Boot, il peut être défini en définissant ServletContextInitializer comme un bean comme suit.

@Bean
public ServletContextInitializer servletContextInitializer() {
    return servletContext -> servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.URL));
}

Activer la réécriture d'URL

Spring Security a une fonctionnalité par défaut qui désactive la réécriture d'URL. Modifiez les paramètres de la classe qui hérite de WebSecurityConfigurerAdapter comme indiqué ci-dessous.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.sessionManagement().enableSessionUrlRewriting(true);
    }
}

Créer une page de connexion

La réécriture d'URL n'est pas disponible sur la page de connexion fournie par Spring Security par défaut, alors créez une page de connexion.

login.html


<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
<form th:action="@{/login}" method="post">
    <div>
        <label>Nom d'utilisateur: <input type="text" name="username"/></label>
    </div>
    <div>
        <label>mot de passe: <input type="password" name="password"/></label>
    </div>
    <input type="submit" value="login"/>
</form>
</body>
</html>

De plus, créez une méthode Controller pour afficher cette page.

@Controller
public class HelloController {
    @GetMapping("/login")
    public String login() {
        return "login";
    }
}

Enfin, ajoutez les paramètres. De plus, le nom d'utilisateur et le mot de passe utilisés pour la connexion sont spécifiés.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/login").permitAll().anyRequest().authenticated()
                .and()
                .formLogin().loginPage("/login")
                .and()
                .sessionManagement().enableSessionUrlRewriting(true);
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("user").password("{noop}user").roles("ADMIN");
    }
}

Par défaut, DelegatingPasswordEncoder est utilisé, donc le mot de passe doit avoir un préfixe. Puisque ce temps est du texte brut, "{noop}" est ajouté.

La recherche sur «DelegatingPasswordEncoder» est résumée ci-dessous. https://qiita.com/d-yosh/items/bb52152318391e5e07aa

Créer une page après la connexion

Cela crée également une méthode html et Controller.

hello.html


<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>
<h1>J'ai pu me connecter</h1>
</body>
</html>
@Controller
public class HelloController {

    @GetMapping("/")
    public String index() {
        return "hello";
    }

    @GetMapping("/login")
    public String login() {
        return "login";
    }
}

Confirmation d'événement

Une erreur se produit lorsque je démarre l'application et y accède. (Pour être exact, une erreur se produit et redirige vers l'écran d'erreur, mais une erreur se produit également et les boucles de redirection.) エラー.png

Si vous regardez le journal, vous pouvez voir que «RequestRefectedException» s'est produite.

org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"

Cause

Spring Security utilise HttpFirewall pour vérifier les demandes, et l'une de ses implémentations, StrictHttpFirewall, rejette les URL contenant un point-virgule. Si vous incluez le JSESSIONID dans l'URL, un point-virgule est ajouté à l'URL, qui est rejetée par StrictHttpFirewall et une exception est déclenchée.

Le HttpFirewall utilisé par défaut diffère selon la version de Spring Security, et dans le passé, DefaultHttpFirewall était utilisé. Cette classe ne rejette pas une demande si l'URL contient un point-virgule. Cette fois, lorsque j'ai répertorié la version, une erreur s'est produite car la valeur par défaut HttpFirewall a changé.

Action 1

Il est possible de changer le paramètre de StrictHttpFirewall pour autoriser le point-virgule.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //Diverses omissions ...

    @Override
    public void configure(WebSecurity web) throws Exception {
        StrictHttpFirewall firewall = new StrictHttpFirewall();
        firewall.setAllowSemicolon(true);
        web.httpFirewall(firewall);
    }

}

Action 2

Modifiez le paramètre pour utiliser DefaultHttpFirewall.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //Diverses omissions ...

    @Override
    public void configure(WebSecurity web) throws Exception {
        DefaultHttpFirewall firewall = new DefaultHttpFirewall();
        web.httpFirewall(firewall);
    }
}

Confirmation

Si vous accédez après avoir effectué l'action 1 ou 2, l'écran de connexion peut être affiché. login.png

Vous pouvez vous connecter avec le nom d'utilisateur: utilisateur et mot de passe: utilisateur. logindekita.png

À propos, la raison pour laquelle JSESSIONID change avant et après la connexion est que les contre-mesures d'attaque de fixation de session de Spring Security sont activées. https://docs.spring.io/spring-security/site/docs/5.1.6.RELEASE/reference/htmlsingle/#ns-session-fixation

à la fin

Les sessions ne doivent pas être gérées par URL.

Recommended Posts

JSESSIONID n'a pas pu être attribué à l'URL lors de l'utilisation de Spring Security
L'histoire selon laquelle la mise à jour forcée n'a pas pu être mise en œuvre
Les ressources statiques ne pouvaient pas être distribuées à partir de WebFlux lorsque @EnableWebFlux était accordé dans Spring Boot
Déployez des applications Spring Boot sur Heroku sans utiliser la CLI Heroku
Le cas où @Autowired n'a pas pu être utilisé dans JUnit5
Les en-têtes de réponse peuvent ne pas être affichés correctement dans Spring Security 4.1
[Résolu dans 5.2.1] Spring Security 5.2.0.RELEASE présente des incompatibilités non mentionnées dans la note de publication
Que faire lorsque les modifications du servlet ne sont pas reflétées
Précautions lors de l'utilisation de Spring AOP avec les classes de ressources Jersery
Le ressort doit être suspecté lorsque les commandes Rails ne fonctionnent pas correctement
Un moyen simple de créer une classe de mappage lors de l'utilisation de l'API
Notez que Spring Data JDBC n'a pas pu insérer
L'histoire de l'arrêt parce que le test des rails n'a pas pu être fait
Comment résoudre l'erreur inconnue apparue lors de l'utilisation de slf4j en Java
L'histoire selon laquelle l'erreur de construction ne s'est pas arrêtée lors de l'utilisation d'Eclipse 2020
[Rails 5] Comment afficher l'écran de changement de mot de passe lors de l'utilisation de l'appareil
Correspondant à "erreur que l'authentification de base ne réussit pas" dans le code de test "L'histoire qui n'a pas pu être faite"
Comment résoudre le problème que le bean n'est pas traité correctement lorsqu'il est imbriqué dans Spring Batch