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?
Une certaine préparation est nécessaire pour reproduire l'événement.
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));
}
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);
}
}
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
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";
}
}
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.)
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 ";"
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é.
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);
}
}
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);
}
}
Si vous accédez après avoir effectué l'action 1 ou 2, l'écran de connexion peut être affiché.
Vous pouvez vous connecter avec le nom d'utilisateur: utilisateur et mot de passe: utilisateur.
À 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
Les sessions ne doivent pas être gérées par URL.