4 Ways to Protect Your Application From Being Hacked

Being hacked is the worst thing that an application can suffer.

Users’ data stolen. All the names, emails, passwords and more.

This breaks the users’ trust in the application. Trust is the only pillar an application has to maintain clients.

But the following 4 tips can protect your application against 90% of the attacks.

And the best is that it can be a simple change in the application.

The critical point here is the user’s data. I have to protect the user’s data everywhere. This means:

  • When travelling through the internet;
  • In the servers where my application is running;
  • And in the database.

Let’s start by protecting the data at travel.

Use HTTPS

HTTPS is the HTTP protocol but secured.

There are a lot of benefits of using HTTPS for a webpage. But I will only focus on two benefits.

And when I talk about adding HTTPS for a webpage, I talk about adding HTTPS for both the frontend and backend communication.

Data Encryption

The HTTPS encrypts all the data sent and received from the website. It’s encrypted before being sent and decrypted at the reception.

Let’s say the user fills out a form with his personal information. When finished, he submits the form to the backend. With HTTP, all the user’s information will go from his browser to the backend visible to anyone who sniffs the network.

This presents a big risk for the user.

Ok, 95% of the people don’t know this risk when using a site with HTTP only. That’s a reason why Google penalize webpages which don’t use HTTPS. To protect those 95% of the people.

Data Integrity

When using HTTPS, I authorize a third-party entity to validate all the data on my webpage. This means that every time a user loads my webpage, the third entity checks if the webpage received by the user is the same as the webpage sent from my server.

If an attacker tries to modify some information (by adding a Javascript file to my webpage), the third-party entity will see this change. Then, the third-party entity will invalidate the communication. And the browser will display nothing.

How to setup HTTPS

To put in place the HTTPS in both frontend and backend, you need to look for a third-party entity to deliver the TLS certificate.

Those entities give you a certificate to add to your servers, both the frontend and the backend.

This can be done easily when using Cloud services or Load Balancers. I won’t enter into the details in this article.

Here is a short list of entities which deliver those certificates:

Use Char Array For Passwords

Now that the user’s data is safe at travel, I have to protect it when using the servers of my application.

Let’s say the user’s request reached my application’s server. If an attacker hacked my server, he can access the received information.

How can he do that?

There are many ways. With Java, a simple memory dump is enough to have access to all variables available in the memory.

Here is a typical controller for login a user.

@PostMapping("/login")
public ResponseEntity<Void> login(@RequestBody UserDto) {
   // validate the credentials
   return ResponseEntity.noContent();
}

And what does the UserDto look like?

public record UserDto(String username, String password) {}

Now, using Jmap, I can dump all the variables and internal state of the application into a single file.

Then, it’s time to look for the values of variables named username and password. And that’s it.

You may say: yes, but this only happens if the hacker has access to the servers.

Do you know everybody who has access to the servers? Did you delete all the old SSH keys? Are you sure old developers have their access blocked? Do you trust your Cloud provider?

Ok, so how do I prevent that?

Just by changing the password type.

public record UserDto(String username, char[] password) {}

Using a char array, the value of the password won’t be in a single place in the Java memory. Each character of the array will be placed in a different memory space.

This means that the hacker needs to read all the characters stored in the application individually. Try different combinations and reorder them.

It’s a lot harder.

Hash all Users’ Passwords

The user’s data is safe from the browser to my backends.

The user’s data is now safe when working with it on my servers.

It’s time to store it in the database. Securely.

If I save the passwords directly in the database, all the work done protecting them on the internet or on the server is wasted. Because if the database is hacked, all the passwords will be accessible to the attacker.

Using a char array as data type in the password column won’t change much.

Instead of storing the passwords clearly in the database, I must store an encrypted password. This way, even if an attacker has access to the database, he won’t be able to read the passwords.

How do I encrypt the passwords before storing them? Which algorithm do I use?

By default, Spring Boot has the BCryptPasswordEncoder to encrypt a password. To configure it, I declare the following bean:

@Component
public class PasswordConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

And to store the encrypted password, I use the encoder this way:

public UserDto register(SignUpDto userDto) {
        Optional<User> optionalUser = userRepository.findByLogin(userDto.login());

        if (optionalUser.isPresent()) {
            throw new Exception("Login already exists");
        }

        User user = userMapper.signUpToUser(userDto);
        user.setPassword(passwordEncoder.encode(CharBuffer.wrap(userDto.password())));

        User savedUser = userRepository.save(user);

        return userMapper.toUserDto(savedUser);
    }

Encoding the password with a Bcrypt algorithm makes it impossible to recover the original value. This algorithm is a one-way hashing algorithm.

This means that once encrypted, I can’t recover the original password from the encrypted value.

But at the login, how do I check if the given password corresponds to the user’s password?

I encrypt the incoming password and check if both encrypted passwords, if both hashed passwords are equal.

Here is how to do it:

public UserDto login(CredentialsDto credentialsDto) {
        User user = userRepository.findByLogin(credentialsDto.login())
                .orElseThrow(() -> new Exception("Unknown user"));

        if (passwordEncoder.matches(CharBuffer.wrap(credentialsDto.password()), user.getPassword())) {
            return userMapper.toUserDto(user);
        }
        throw new Exception("Invalid password");
    }

Use A Separated Project for Configuration

Now, all the user’s data is safe. From the browser to the database.

With the previous 3 steps, I’ve protected my application against external attackers.

But there is another kind of attacker. Internal attackers.

What if the developers try to steal the users’ data?

You may say: I trust my team, I’ve been working with them for years.

But what about the newcomers?

I can’t blindly trust everyone. That’s why I don’t give the passwords to everyone. But many passwords are stored in the application. Everyone who has access to the code has access to the passwords.

To solve that, I use an external project for the configurations. Not all the configurations, just the critical ones.

In my production code, I use environment variables when I need a password. Like the following code:

spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}

And when do I inject those values? When do I send the environment variables?

I have some options.

The first one and most complete (and complex) is using a distributed configuration service. As described in this article. But this needs another service, register it in the load balancer, change the bootstrap startup of my application…

An alternative is to use a Vault-like system or Secrets Manager from the Cloud provider where to store all the secrets. But this also requires another service and a new connection.

The alternative I use is the following. I inject the values as environment variables in the last step of my deployment phase in my CI/CD platform.

I can store secrets in my CI/CD platform. Only accessible for Admin users. When deploying my application, with AWS ECS or AWS EC2, I add some environment variables that I read from the CI/CD platform.

This way, the passwords are stored securely, and there is restricted access to them. For the staging environments, I can use default values to let all the developers have access to them as follows:

spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://${DB_URL:my-staging-database}
    username: ${DB_USERNAME:username-staging}
    password: ${DB_PASSWORD:password-staging}

Conclusion

The goal of this article is to show 4 easy ways to increase the security of an application. None of those steps need architectural changes. All the steps are easy to put in place and protect against 90% of the attacks.


Never Miss Another Tech Innovation

Concrete insights and actionable resources delivered straight to your inbox to boost your developer career.

My New ebook, Best Practices To Create A Backend With Spring Boot 3, is available now.

Best practices to create a backend with Spring Boot 3

Leave a comment

Discover more from The Dev World - Sergio Lema

Subscribe now to keep reading and get access to the full archive.

Continue reading