This article is part of a series “Secure code development” in which I would like to show some tips and good practices for developers to help You build more secure software. Targeted audiences are IT students and junior developers but hopefully, anyone inside the IT industry would read something interesting. Provided examples are created using java and the spring-boot framework.

TL;DR;

Password management within the code base is critical. You should never allow the password to be stored in plain text within both database and code. For storing authentication data use secure hashing functions for example BCrypt. For Reusable secrets use CI Variables, config server or HashiCorp Vault.

Password Management

If a non-technical person asks an IT security professional about a tip on how to be more secure in the cyber world – probably the most important answer would be:

“Keep your passwords secure. Use different passwords for different services. Use strong and complicated passwords”

Some security guy

And it was easy for us to say. It was hard to encourage people to care about this topic. Just read about the CCleaner breach. It all started with a developer who used the same password for multiple services. But for a long time already the things have changed. There are plenty of Password Managers available on a market (some of them shared as open-source software) for free for personal use. They are really easily integrated with browsers and applications. There is a possibility to synchronize the manager with the smartphone and use configured passwords on both personal computers and a smartphone.  And the answer to the most frequent allegation “how to remember all these passwords” is “You don’t have to, there is a software that can do it for You”.

There is a reason for mentioning this. The reply to the question above asked by the software developer would be the same. The context would be different. 

In the further text, I would use a “secret” keyword for password, token and keys.

In software development there are two types for secrets considering the use:

  • Used for user/service authentication 
  • Used by software to authenticate in remote service

I will not describe a process to integrate with 3rd party authentication using Facebook, Google, GitHub or so on.

Ideal Solution – Use x509 Client Certificates (for both user auth and auth in remote service)

Use SSL/TLS Client certificate authentication and convince others to implement it as well. So basically here You have your truststore set (in most of the scenarios it is JRE’s cacert) where are stored trusted CA certificates. Whenever a request signed by a certificate issued by CA on the list it will be accepted and the user with proper CommonName (CN=) can be authorized (or not) to use service. This way Your application will only be able to verify the client using a properly signed certificate (any request signed by not trusted by JRE’s cacerts certificate will be rejected).

SSL/TLS Client Authentication
Source: https://comodosslstore.com/blog/what-is-ssl-tls-client-authentication-how-does-it-work.html

It Is really easy to build a service that will use Client Certificate authentication using Spring Security.

application.properties

ssl.server.client-auth=want

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class X509SecurityAdapter extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .x509()
                .subjectPrincipalRegex("CN=(.*?)(?:,|$)")
                .userDetailsService(userDetailsService())
                .and()
                .exceptionHandling()
                .accessDeniedPage("/forbidden");

    }

    @Bean
    public UserDetailsService userDetailsService() {
        return new UserDetailsService() {
            @Override
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                return new User(username, "", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_API"));
            }
        };
    }
}

You must remember here to avoid using self-signed certificates. They are easy to generate and use on development environment but in order to make use of them You have to disable multiple options like SSL Verification which never should be disabled.

User/service authentication

Context to implement:

  1. User us creating an account (providing a password)
  2. Software is storing  the provided password
  3. User use credentials in order to log in
  4. Software is verifying if user provided correct password

It must be stated here that software must never store password in database in format which user provided (plaintext). 

This topic is pretty straight forward and almost each modern framework provides functionalities to make it secure. Password should be stored in database in hash form using secure algorithm (no MD5!). Hashes are being used because there are one direction function. Once You create a hash for a particular string You should never been able to retrieve a actual password from it.

// Declaration for BCrytpPasswordEncoder
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
Private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
public AuthService(PasswordEncoder passwordEncoder, UserRepository userRepository) {
	this.passwordEncoder = passwordEncoder;
	this.userRepository = userRepository;
}
 
public User register(UserDto accountDto) {
    User user = new User(accountDto);
    user.setPassword(passwordEncoder.encode(accountDto.getPassword()));
    return userRepository.save(user);
}

Using above pseudocode user would have password set like follows

Source: https://liveoncode.com/how-to-decrypt-hash-stored-by-bcrypt/

Bcrypt function is automatically generating random hash and concatenate it with a password. Also, there is a possibility to implement the number of iterations hashing algorithms will be executed (above example 11 iterations).

The operation which verifies if a user is providing the correct password is counting hash of an entered password and then it compares newly created hash with hash from the database. 

if (bCryptPasswordEncoder.matches(passwordAuthModel.getPassword(), user.get().getPassword()))
    return “authenticated”;

As easy as it is, it is enough to make sure that even if the database would leak somewhere, bad guys won’t have an easy job to retrieve passwords from such a table.

Authenticate in remote service

Things are different when You need to reuse saved password so You will be properly authenticated within another resource. For example, the application needs the password to login to the database or apiKey for REST API used by mobile applications. Most of today’s applications need a connection with remote systems. And each such connection has to be authenticated. You must remember to never put such secrets within codebase (even if there is a history in git with the previous commit which contains hardcoded passwords – change it now).

There are number of solutions to audit Your codebase for hardcoded secrets

What to do instead? There are multiple solutions to avoid hardcoding passwords:

  1. Use CI Variables for storing secrets which are used inside of application build

Both GitLab and GitHub offer a place to store securely secrets:

source: GitHub

2. Use HashiCorp Vault

Require more configuration as it is another software that can take charge of secure storage of secrets.

Source: https://www.hashicorp.com

It is really easy to configure and run Vault from docker which is available via DockerHub – https://hub.docker.com/_/vault

running vault:

docker run --cap-add=IPC_LOCK -e 'VAULT_LOCAL_CONFIG={"backend": {"file": {"path": "/vault/file"}}, "default_lease_ttl": "168h", "max_lease_ttl": "720h"}' vault server

from now on it is available on http://localhost:8200

Spring Integration with Vault:

private final VaultOperations vaultOperations;
@Autowired
public VaultHelper (VaultOperations vaultOperations){
    this.vaultOperations = vaultOperations;

}
/**
 * Saves password at path secret/ for fiven user within Vault.
 *
 * @param password password to save within vault
 * @param token token which will be stored as accessible parameter
 */
public void savePassword(String password, String token){
    Map<String, String> upassMap = new HashMap<>();
    upassMap.put("password", password);
    vaultOperations.write("secret/" + token, upassMap);
}
/**
 * Getting password from location secret/token on form of a String
 *
 * @param passwordLoc String needed to accessing password value from Vault
 * @return String password
 */
public String getPassword(String passwordLoc){
    VaultResponseSupport<Map<String, Object>> password = vaultOperations.read("secret/" + passwordLoc);
    assert password != null;
    return Objects.requireNonNull(password.getData()).get(Constants.PASSWORD).toString();
}

3. Use Spring Cloud config

This solution can be used with both CI Variables and HashiCorp Vault. Config servers are able to store and serve configuration of the same application for multiple environments and profiles. You can store configuration for dev, int and prod environment within a single config server and in the end, build an app using proper one inside Your CICD pipeline. It requires some effort in order to be installed and configured but the properly set infrastructure can be easily accessed by developers. No more problems migrating from one environment to another.

Conclusions

You can use both CI variables and Vault for almost every solution (PHP, Python, .net and more). You can see it is really easy to avoid using hardcoded passwords. Althoguh with even the strongest password management procedures developed within the process of software delivery, no authentication method would be stronger then TLS/SSL Certificate Client Authentication.

References:
https://www.hashicorp.com
https://github.com
https://hub.docker.com/_/vault
https://cloud.spring.io/spring-cloud-config/reference/html/
https://cloud.spring.io/spring-cloud-vault/reference/html/
Registration with Spring Security – Password Encoding

Comments are closed