Reputation: 246
I'm using a springboot thymeleaf application with csrf enabled. In order to prevent my application from the XSS Vulnerability I added a filter following this tutorial. I have copied the files for XSSFilter, XSSRequestWrapper and XSSUtils as is from here. Possibly the only new thing that I have added in my strip function is this code:
String returnValue = Jsoup.clean(value, Whitelist.none());
returnValue = Parser.unescapeEntities(returnValue, true);
return returnValue;
I had to add this because Jsoup clean was adding amp; in place of &.
I have a login page for my application and only if the user is successfully authenticated, is he allowed in. The application works absolutely fine without the filter. However, when I add the filter the application won't login and won't show any error whatsoever. Here is my index.html in which thymeleaf inserts the csrf token:
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head id="Head1" >
<title>Home</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"/>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE11"/>
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
<link rel="stylesheet" th:href="@{css_page_specific/index.css}">
<link rel="stylesheet" th:href="@{css_general/toggle-button-modern.css}">
<script type="text/javascript" th:src="@{commons.js}"></script>
</head>
<form method="post" id="init-form2" th:action="@{/login}" style="max-width: 350px; margin: 0 auto;">
<div class="form-group">
<input type="text" name="username" id="ad-auth-username" placeholder="id">
</div>
<div class="form-group">
<input type="password" name="password" id="ad-auth-password" placeholder="Password">
</div>
Here is my Login Controller:
@Controller
public class LoginController {
@GetMapping("/login")
public String showLoginForm(Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || authentication instanceof AnonymousAuthenticationToken) {
return "login";
}
return "redirect:/";
}
@GetMapping("/logout")
public String logoutPage(HttpServletRequest request, HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
new SecurityContextLogoutHandler().logout(request, response, auth);
}
return "redirect:/";
}
}
Here is my web security config:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requiresChannel().anyRequest().requiresSecure().and().authorizeRequests().antMatchers("/login","/css_general/**","/css_page_specific/**","/**/*.js","/**/*.css")
.permitAll()
.anyRequest()
.authenticated()
.and().formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/index.html")
.failureUrl("/login?error=true")
)
.sessionManagement().invalidSessionUrl("/login")
.and()
.httpBasic()
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login").invalidateHttpSession(true).deleteCookies("JSESSIONID")
.permitAll()
.and()
.cors().disable()
.headers()
.xssProtection()
.and()
.contentSecurityPolicy("script-src 'self'");
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css_general/**", "/css_page_specific/**","/resources/static/css_general/**","/resources/static/css_page_specific/**","/static/css_page_specific/**","/static/css_general/**");
}
Now in my filter, if I don't put the xss stripping logic and put a breakpoint, I see it contains 3 parameters:
username, password and _csrf.
This filter chain goes through and is ultimately allowed passed to csrfFilter which succeeds. I am able to successfully login.
However, if I put in the XSSFilter code, I don't see any of the 3 parameters being passed and ultimately the csrf filter fails and I stay on the login page without any error message. I know the filter is called for each request but I have checked every request and I just don't see these parameters being passsed and that's what I suspect is failing the csrfFilter.
How can I have the xss filter work and successfully log in? I'm using thymeleaf only for the login page and rest of the pages just exchange data with the server through regular json request and responses.
TLDR: Successfully able to authenticate without adding XSSFilter. Receiving 3 parameters in the filter - username, password and _csrf. Unable to authenticate and login after adding XSSFilter - Receiving 0 parameters in the filter. How can I restrict all input passed to the application to valid, allowlisted content, and ensure that all response/output sent by the server is HTML/URL/JavaScript encoded, depending on the context in which the data is used by the application?
Upvotes: 1
Views: 1004
Reputation: 18559
String returnValue = Jsoup.clean(value, Whitelist.none());
returnValue = Parser.unescapeEntities(returnValue, true);
return returnValue;
Adding unescapeEntities
here defeats the purpose of the filter. As an input of <script>alert(1)</script>
will give the return of <script>alert(1)</script>
.
The approach of these filters isn't reliable. A lot of XSS vulnerabilities don't require HTML tags as they're in an attribute or JavaScript context instead, and parsing non-HTML values as if they're HTML will cause further issues as it can silently corrupt valid input data.
For XSS prevention, I would focus on ensuring all values are escaped correctly in the templates. Thymeleaf should be doing this anyway so a filter wouldn't be adding anything.
Upvotes: 1
Reputation: 1573
https://www.baeldung.com/spring-reading-httpservletrequest-multiple-times
Maybe this link can be useful for your problem.
public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {
private byte[] cachedBody;
public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
super(request);
InputStream requestInputStream = request.getInputStream();
this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);
}
}
After reading to cachedBody, you can check XSS.
Upvotes: 0