Beim Aktualisieren von Spring Security in einer Anwendung, die die JSESSIONID in der URL enthält, tritt jetzt ein Fehler auf. Ich habe die Ursache untersucht und werde sie zusammenfassen.
Warum nicht jetzt JSESSIONID in die URL aufnehmen?
Einige Vorbereitungen sind erforderlich, um das Ereignis zu reproduzieren.
Bei einem Browser, der Cookies verwenden kann, wird JSESSIONID mithilfe von Cookies verwaltet. Ändern Sie die Einstellungen des Servlet-Containers, um die Verwaltung per URL zu erzwingen.
Wenn Sie Spring Boot verwenden, können Sie dies festlegen, indem Sie "ServletContextInitializer" wie folgt als Bean definieren.
@Bean
public ServletContextInitializer servletContextInitializer() {
return servletContext -> servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.URL));
}
Spring Security verfügt über eine Standardfunktion, die das Umschreiben von URLs deaktiviert. Ändern Sie die Einstellungen in der Klasse, die "WebSecurityConfigurerAdapter" erbt, wie unten gezeigt.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().enableSessionUrlRewriting(true);
}
}
Das Umschreiben von URLs ist auf der von Spring Security bereitgestellten Anmeldeseite standardmäßig nicht verfügbar. Erstellen Sie daher eine Anmeldeseite.
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>Nutzername: <input type="text" name="username"/></label>
</div>
<div>
<label>Passwort: <input type="password" name="password"/></label>
</div>
<input type="submit" value="login"/>
</form>
</body>
</html>
Erstellen Sie außerdem eine Controller-Methode, um diese Seite anzuzeigen.
@Controller
public class HelloController {
@GetMapping("/login")
public String login() {
return "login";
}
}
Fügen Sie abschließend die Einstellungen hinzu. Außerdem werden der Benutzername und das Kennwort für die Anmeldung angegeben.
@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");
}
}
Standardmäßig wird "DelegatingPasswordEncoder" verwendet, daher muss das Kennwort ein Präfix haben. Da es sich diesmal um Klartext handelt, wird "{noop}" hinzugefügt.
Die Forschung zu "DelegatingPasswordEncoder" ist unten zusammengefasst. https://qiita.com/d-yosh/items/bb52152318391e5e07aa
Dadurch wird auch eine HTML- und Controller-Methode erstellt.
hello.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Hello</title>
</head>
<body>
<h1>Ich konnte mich einloggen</h1>
</body>
</html>
@Controller
public class HelloController {
@GetMapping("/")
public String index() {
return "hello";
}
@GetMapping("/login")
public String login() {
return "login";
}
}
Ein Fehler tritt auf, wenn ich die Anwendung starte und darauf zugreife. (Um genau zu sein, tritt ein Fehler auf und leitet zum Fehlerbildschirm weiter, aber es tritt auch ein Fehler auf und die Umleitungsschleifen.)
Wenn Sie sich das Protokoll ansehen, können Sie feststellen, dass eine "RequestRefectedException" aufgetreten ist.
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
Spring Security verwendet "HttpFirewall", um Anforderungen zu überprüfen, und eine seiner Implementierungen, "StrictHttpFirewall", lehnt URLs ab, die Semikolon enthalten. Wenn Sie die JSESSIONID in die URL aufnehmen, wird der URL ein Semikolon hinzugefügt, das von "StrictHttpFirewall" abgelehnt wird, und eine Ausnahme wird ausgelöst.
Die standardmäßig verwendete "HttpFirewall" unterscheidet sich je nach Version von Spring Security. In der Vergangenheit wurde "DefaultHttpFirewall" verwendet. Diese Klasse lehnt eine Anforderung nicht ab, wenn die URL ein Semikolon enthält. Dieses Mal, als ich die Version auflistete, trat ein Fehler auf, weil sich die Standard-HttpFirewall geändert hat.
Es ist möglich, die Einstellung von "StrictHttpFirewall" zu ändern, um ein Semikolon zuzulassen.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//Verschiedene Auslassungen ...
@Override
public void configure(WebSecurity web) throws Exception {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowSemicolon(true);
web.httpFirewall(firewall);
}
}
Ändern Sie die Einstellung, um "DefaultHttpFirewall" zu verwenden.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//Verschiedene Auslassungen ...
@Override
public void configure(WebSecurity web) throws Exception {
DefaultHttpFirewall firewall = new DefaultHttpFirewall();
web.httpFirewall(firewall);
}
}
Wenn Sie nach Aktion 1 oder 2 darauf zugreifen, kann der Anmeldebildschirm angezeigt werden.
Sie können sich mit Benutzername: Benutzer und Passwort: Benutzer anmelden.
Der Grund, warum sich JSESSIONID vor und nach der Anmeldung ändert, ist übrigens, dass die Gegenmaßnahmen für Sitzungsfixierungsangriffe von Spring Security aktiviert sind. https://docs.spring.io/spring-security/site/docs/5.1.6.RELEASE/reference/htmlsingle/#ns-session-fixation
Sitzungen sollten nicht per URL verwaltet werden.
Recommended Posts