Spring Boot how to hide passwords in properties file
You can use Jasypt to encrypt properties, so you could have your property like this:
db.password=ENC(XcBjfjDDjxeyFBoaEPhG14wEzc6Ja+Xx+hNPrJyQT88=)
Jasypt allows you to encrypt your properties using different algorithms, once you get the encrypted property you put inside the ENC(...)
. For instance, you can encrypt this way through Jasypt using the terminal:
encrypted-pwd$ java -cp ~/.m2/repository/org/jasypt/jasypt/1.9.2/jasypt-1.9.2.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="contactspassword" password=supersecretz algorithm=PBEWithMD5AndDES
----ENVIRONMENT-----------------
Runtime: Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 24.45-b08
----ARGUMENTS-------------------
algorithm: PBEWithMD5AndDES
input: contactspassword
password: supersecretz
----OUTPUT----------------------
XcBjfjDDjxeyFBoaEPhG14wEzc6Ja+Xx+hNPrJyQT88=
To easily configure it with Spring Boot you can use its starter jasypt-spring-boot-starter with group ID com.github.ulisesbocchio
Keep in mind, that you will need to start your application using the same password you used to encrypt the properties. So, you can start your app this way:
mvn -Djasypt.encryptor.password=supersecretz spring-boot:run
Or using the environment variable (thanks to spring boot relaxed binding):
export JASYPT_ENCRYPTOR_PASSWORD=supersecretz
mvn spring-boot:run
You can check below link for more details:
https://www.ricston.com/blog/encrypting-properties-in-spring-boot-with-jasypt-spring-boot/
To use your encrypted properties in your app just use it as usual, use either method you like (Spring Boot wires the magic, anyway the property must be of course in the classpath):
Using @Value
annotation
@Value("${db.password}")
private String password;
Or using Environment
@Autowired
private Environment environment;
public void doSomething(Environment env) {
System.out.println(env.getProperty("db.password"));
}
Update: for production environment, to avoid exposing the password in the command line, since you can query the processes with ps
, previous commands with history
, etc etc. You could:
- Create a script like this:
touch setEnv.sh
- Edit
setEnv.sh
to export theJASYPT_ENCRYPTOR_PASSWORD
variable#!/bin/bash
export JASYPT_ENCRYPTOR_PASSWORD=supersecretz
- Execute the file with
. setEnv.sh
- Run the app in background with
mvn spring-boot:run &
- Delete the file
setEnv.sh
- Unset the previous environment variable with:
unset JASYPT_ENCRYPTOR_PASSWORD
To the already proposed solutions I can add an option to configure an external Secrets Manager
such as Vault.
- Configure Vault Server
vault server -dev
(Only for DEV and not for PROD) - Write secrets
vault write secret/somename key1=value1 key2=value2
- Verify secrets
vault read secret/somename
Add the following dependency to your SpringBoot project:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-vault-config</artifactId>
</dependency>
Add Vault config properties:
spring.cloud.vault.host=localhost
spring.cloud.vault.port=8200
spring.cloud.vault.scheme=http
spring.cloud.vault.authentication=token
spring.cloud.vault.token=${VAULT_TOKEN}
Pass VAULT_TOKEN
as an environment variable.
Refer to the documentation here.
There is a Spring Vault project which is also can be used for accessing, storing and revoking secrets.
Dependency:
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
</dependency>
Configuring Vault Template:
@Configuration
class VaultConfiguration extends AbstractVaultConfiguration {
@Override
public VaultEndpoint vaultEndpoint() {
return new VaultEndpoint();
}
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication("…");
}
}
Inject and use VaultTemplate:
public class Example {
@Autowired
private VaultOperations operations;
public void writeSecrets(String userId, String password) {
Map<String, String> data = new HashMap<String, String>();
data.put("password", password);
operations.write(userId, data);
}
public Person readSecrets(String userId) {
VaultResponseSupport<Person> response = operations.read(userId, Person.class);
return response.getBody();
}
}
Use Vault PropertySource
:
@VaultPropertySource(value = "aws/creds/s3",
propertyNamePrefix = "aws."
renewal = Renewal.RENEW)
public class Config {
}
Usage example:
public class S3Client {
// inject the actual values
@Value("${aws.access_key}")
private String awsAccessKey;
@Value("${aws.secret_key}")
private String awsSecretKey;
public InputStream getFileFromS3(String filenname) {
// …
}
}
UPDATE: I noticed folks down-voting this, so I have to say that although this is not an ideal solution, but this works and acceptable in some use-cases. Cloudfoundry uses Environment variables to inject credentials when a Service is binded to an application. More info https://docs.cloudfoundry.org/devguide/services/application-binding.html
And also if your system is not shared, then for local development this is also acceptable. Of course, the more safe and secure way is explained in Answer by @J-Alex.
Answer:
If you want to hide your passwords then the easiest solution is to use Environment variables in application.properties
file or directly in your code.
In application.properties
:
mypassword=${password}
Then in your configuration class:
@Autowired
private Environment environment;
[...]//Inside a method
System.out.println(environment.getProperty("mypassword"));
In your configuration
class:
@Value("${password}")
private String herokuPath;
[...]//Inside a method
System.out.println(herokuPath);
Note: You might have to restart after setting the environment variable. For windows:
Refer this Documentation for more info.