Fixing OWASP Top 10 In Spring Boot, MVC, Data, and Security

by Didin J., updated on Jul 23, 2020 Fixing OWASP Top 10 In Spring Boot, MVC, Data, and Security

A comprehensive step by step tutorial and guide to fixing the OWASP top 10 vulnerabilities in Spring Boot, MVC, Data, and Security

In this tutorial, we will show you the step by step guide to fixing each of the OWASP top 10 vulnerabilities in Java web application that builds by Spring Boot, MVC, Data, and Security. We will start from the web application development, deployment, penetration testing, and fix the vulnerabilities issue based on OWASP top ten vulnerabilities.

This tutorial divided into several steps:

The following tools, frameworks, libraries, and modules are required for this tutorial:

  1. Spring Boot, MVC, Data, Security, Thymeleaf, and H2 Database
  2. Web Server (Nginx and Tomcat7)
  3. OWASP ZAP application
  4. Terminal or CMD
  5. Text Editor or IDE

Let's get started with the main steps!


Step #1: Download Existing Spring Boot, MVC, Data and Security Web Application

We will use the existing Spring Boot, MVC, Data, and Security Web Application that previously created in our other tutorial. Clone this GitHub source.

git clone https://github.com/didinj/spring-boot-security-user-role-login-eclipse.git mynotes

Build this Spring Boot application by type this command inside this application folder.

cd mynotes
./gradlew build

That Gradle builds command will create a .war file in the build/libs/ folder.


Step #2: Deploy Web Application to VPS

We will use Tomcat 9 and Nginx as a reverse proxy for Tomcat 9. Make sure you have installed both of them. We are installing both the HTTP server and Container in Ubuntu VPS. We already make a tutorial for installing Nginx and Tomcat on Ubuntu VPS. The Tomcat version is different but the configuration remain the same. 

Next, transfer the war file to your Ubuntu VPS.

scp NetBeansProjects/mynotes/build/libs/mynotes-0.0.1-SNAPSHOT.war [email protected]:~/

Connect to the VPS using SSH.

ssh [email protected]

Enter the password that you are using for the server user. Copy the transferred war file to the Tomcat webapps folder.

sudo cp mynotes-0.0.1-SNAPSHOT.war /opt/tomcat/webapps/

Open the Tomcat 9 server.xml with a text editor.

sudo nano /opt/tomcat/conf/server.xml

Then replace the following tags.

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

        <!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
        <!--
        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

        <!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

      </Host>

With these tags to move the current document folder to the new web application.

      <Host name="localhost" appBase="webapps" unpackWars="true" autoDeploy="true">
        <Logger className="org.apache.catalina.logger.FileLogger" directory="logs" prefix="virtual_log." suff$
        <Context path="" docBase="/opt/tomcat/webapps/mynotes-0.0.1-SNAPSHOT" debug="0" reloadable="true" />
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="virtual_log." s$
      </Host>

Next, open the Nginx configuration file using a text editor.

sudo nano /etc/nginx/sites-enabled/default

Then change the server root document to this line.

server {
   ...
   root /opt/tomcat/webapps/mynotes-0.0.1-SNAPSHOT;
}

Restart Tomcat and Nginx server.

sudo systemctl restart tomcat
sudo service nginx restart

Now, the web application should accessible from other computers using http://ip_address. For example, we are using IP address 192.168.0.100, on the browser go to this http://192.168.0.100, and here's the web application looks like.

Fixing OWASP Top 10 In Spring Boot, MVC, Data, and Security - Web App Example


Step #3: Scan using OWASP ZAP on Basis Web Application

We will scan this basic Spring Boot, MVC, Data, Security web application to find the vulnerabilities. For that, install the OWASP ZAP application (not working on MACOS Catalina) then install it on your computer. Start the OWASP ZAP application, and you will get this application like this.

Fixing OWASP Top 10 In Spring Boot, MVC, Data, and Security - Owasp Zap

Before running an attack on the website, set the OWASP ZAP to the maximum attack by open the Analyse menu -> Scan Policy Manager.

Fixing OWASP Top 10 In Spring Boot, MVC, Data, and Security - scan policy

Click modify then set Policy like this screenshot then click OK button.

Fixing OWASP Top 10 In Spring Boot, MVC, Data, and Security - scan policy settings

Choose the Automated Scan button on the main panel. Then fill the URL to attack field with "http://192.168.0.1" and leave other options as default.

Fixing OWASP Top 10 In Spring Boot, MVC, Data, and Security  - quick attack

Click the Attack button to start scanning. After scanning is complete, it will show alerts in the bottom panel.

Fixing OWASP Top 10 In Spring Boot, MVC, Data, and Security - add site

You can click the attack button again to make sure get more vulnerabilities results. To see the full details scan results, open the Report menu then Generate an HTML report. Save the HTML file to your desired location then open it in the browser. The result should be like this.

Fixing OWASP Top 10 In Spring Boot, MVC, Data, and Security - result-1

result-2

result-3

result-4

result-6

result-7

result-8


Step #4: Fix the Vulnerabilities Issues

As you can see in the reports, there is 1 alert with a high-risk level and 5 alerts with the low-risk level. It also has a solution for them. Let's fix those issues one by one.

Fixing SQLInjection

SQL injection vulnerability found on the Signup form is more precisely in the email field. To find out, first, let's check the code of AuthController.java in the Spring Boot application.

...

@Controller
public class AuthController {

    @Autowired
    private CustomUserDetailsService userService;

...

    @RequestMapping(value = "/signup", method = RequestMethod.POST)
    public ModelAndView createNewUser(@Valid User user, BindingResult bindingResult) {
        ModelAndView modelAndView = new ModelAndView();
        User userExists = userService.findUserByEmail(user.getEmail());
        if (userExists != null) {
            bindingResult.rejectValue("email", "error.user",
                    "There is already a user registered with the username provided");
        }
        if (bindingResult.hasErrors()) {
            modelAndView.setViewName("signup");
        } else {
            userService.saveUser(user);
            modelAndView.addObject("successMessage", "User has been registered successfully");
            modelAndView.addObject("user", new User());
            modelAndView.setViewName("login");

        }
        return modelAndView;
    }

}

In the Signup method, there is a method to find the user by email from the UserDetailsService.

User userExists = userService.findUserByEmail(user.getEmail());

Next, check the CustomUserDetailsService.java for the findUserByEmail() method.

    public User findUserByEmail(String email) {
        return userRepository.findByEmail(email);
    }

That method use findByEmail from UserRepository.java that extends JpaRepository.

User findByEmail(final String email);

So, it's very clear that we use a standard query with a clear text parameter which is too easy for attackers to attack using SQL injection. The findByEmail translated from this query.

select u from User u where u.email = ?1

So, we need to change the Spring Data JPA Named Parameters like this.

    @Query("SELECT u FROM User u WHERE u.email = :email")
    User findUserByEmail(@Param("email") String email);

Don't forget to organize all the required imports. Also, change the method in CustomUserDetailsService.java.

    public User findUserByEmail(String email) {
        return userRepository.findUserByEmail(email);
    }

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {

        User user = userRepository.findUserByEmail(email);
        if (user != null) {
            List<GrantedAuthority> authorities = getUserAuthority(user.getRoles());
            return buildUserForAuthentication(user, authorities);
        } else {
            throw new UsernameNotFoundException("username not found");
        }
    }

Apply this way to all of class and methods that use standard JPQL. In this example, there is NoteRepository.java.

    @Query("SELECT n FROM Notes n WHERE n.title = :title")
    Notes findByTitle(@Param("title") String title);

And RoleRepository.java.

    @Query("SELECT r FROM Role r WHERE r.role = :role")
    Role findByRole(@Param("role") String role);

Fix Absence of Anti-CSRF Tokens

In the Spring Security application, CSRF protection is enabled by default. In this application, the CSRF protection disabled. For that, remove the http.csrf().disabled() in the WebSecurityConfig.java. So, the configuration looks like this.

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/").hasAuthority("ADMIN").antMatchers("/h2/console").permitAll()
                .antMatchers("/login").permitAll().antMatchers("/signup").permitAll().antMatchers("/notes")
                .hasAuthority("ADMIN").antMatchers("/notes/**").hasAuthority("ADMIN").anyRequest().authenticated().and()
                .formLogin().successHandler(customizeAuthenticationSuccessHandler).loginPage("/login")
                .failureUrl("/login?error=true").usernameParameter("email").passwordParameter("password").and().logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/").and()
                .exceptionHandling();

In the HTML template that we are using Thymeleaf, check if there's no Thymeleaf implementation on each form. In the login and signup form, we found the unnecessary form that not using  Thymeleaf th:action. For that, we are replace that from with <div> and <a> tag.

login.html

            <div class="form-signin">
                <a class="btn btn-md btn-success btn-block" th:href="@{/signup}">Signup Here</a>
            </div>

signup.html

            <div class="form-signin">
                <a class="btn btn-md btn-success btn-block" th:href="@{/login}">Sign In</a>
            </div>

Also, in show.html, the form not using th:action tag. For that, add th:action for that form.

                <form th:action="@{/notes/delete}">
                    <input type="hidden" name="id" th:value="${note.id}" />
                    <h2><input type="submit" class="btn btn-danger" value="Delete" onclick="return confirm('Are you sure?');" />
                        <a th:href="@{'/notes/edit/' + ${note.id}}" class="btn btn-warning"><i class="fas fa-edit"></i> Edit</a></h2>
                </form>


Fixing Cookie Without SameSite Attribute, Cookie Without Secure Flag, and Incomplete or No Cache-control and Pragma HTTP Header Set

Implementation of fixing cookie with the same-site attribute, Cookie Without Secure Flag, and Incomplete or No Cache-control and Pragma HTTP Header Set requires to serve web application using HTTPS and Nginx ModSecurity module. For that, make sure the web application using TLS/SSL certificate and serve over HTTPS. To get TLS/SSL certificate, you must have your own domain. 

To install the ModSecurity module for Nginx, you can find the guide from their official site https://www.nginx.com/blog/compiling-and-installing-modsecurity-for-open-source-nginx/. Also, install the required modules fo HTTPS, ModSecurity, and Cookies Control by reconfiguring the Nginx source.

sudo ./configure --user=www-data --group=www-data --with-pcre-jit --with-debug --with-http_ssl_module --with-http_realip_module --with-http_v2_module --add-module=../ModSecurity-nginx --add-module=../nginx_cookie_flag_module
sudo make
sudo make install

You can get the final Nginx configuration from our GitHub repo.


Step #5: Re-Testing The Web Application

To re-testing, the web application using the OWASP ZAP application, do the same step as the previous OWASP ZAP scan. And here the new results.

Fixing OWASP Top 10 In Spring Boot, MVC, Data, and Security - final result

As you see in the OWASP ZAP result, there's no alert found. That's means, all of the vulnerabilities issues found by OWASP ZAP already fixed.

In conclusion, to make a secure web application, we need to configure all aspects of the live or production web application. They are web application codes, container servers, and HTTP servers. Keep in mind that HTTPS is a mandatory requirement for a web application that accessible to the public.

That it's, Fixing OWASP Top 10 In Spring Boot, MVC, Data, and Security. You can get the full fixed Spring Boot application source code from our GitHub.

That just the basic. If you need more deep learning about Java and Spring Framework you can take the following cheap course:

Thanks!

Loading…