Take a look at a typical configuration file

. Omit...# configure MySQL database connectionSpring. The datasource. The driver - class - name = com. Mysql. JDBC. Driver spring. The datasource. Url = JDBC: mysql: / / 121.196. XXX. XXX: 3306 / user? useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456

# configure Redis cache connectionRedis. Host = 121.196. XXX. XXX redis. Port = 6379 redis. Password = 111111# configure the SMS service connectionali.sms.access_key_id=2zHmLdxAes7Bbe2w ali.sms.access_key_secret=bImWdv6iy0him8ly ... Omit...Copy the code

This is an excerpt from the application.properties configuration file for a typical Spring Boot project.

SHH… Tell me between you, do a lot of friends also write this way?

This seems fine at first glance, and many people take it for granted. I myself have seen many projects (including many open source projects) write this way.

But on closer examination, I found:

Yes! Many project configuration files, including database passwords, cache passwords, or some third party service keys are directly matched inside, without any encryption processing!

Some people say that this profile is my own anyway, what’s the risk?

For example, a programmer uploaded his company’s project code to his GitHub repository and forgot to process the configuration file. As a result, the company database was leaked. The key problem is that the company is also a hotel management company.

On the other hand, if all the important information in the project’s configuration file had been encrypted, this would probably not have happened. So, even in the project configuration file, important information has to be encrypted!


What information should be encrypted?

In general, all configuration items (or fields) related to information security should be handled in the project configuration file. Typical examples are:

  • Used database, cache password
  • The password of the middleware and message queue used
  • Access_keys used by various third-party services
  • Communication information of other third party services
  • . , etc.

In general, key fields should be protected, or at least not written in plain text in configuration files!


How do you encrypt configuration items?

The method is very simple and can be completed in a few steps. Let’s first demonstrate the simplest version:

1. Firstly, establish a basic Spring Boot project

I won’t go into that again

2, the introduction ofjasypt-spring-bootEncryption component

Jasypt is a powerful encryption library introduced by jasypt-spring-boot, an out-of-the-box encryption component

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>
Copy the code

3. Configure the encryption key

Add the following configuration to the Spring Boot project configuration file application.properties:

jasypt.encryptor.password=CodeSheep
Copy the code

Jasypt uses this custom encryption key to encrypt important items in the configuration file.

4. Encryption test

To facilitate testing, we extend the startup class of the Spring Boot project directly, execute the encrypted test code when the project starts, and see the effect directly

@SpringBootApplication
public class SpringBootConfigEncryptApplication implements CommandLineRunner {

    @Autowired
    private ApplicationContext appCtx;

    @Autowired
    private StringEncryptor codeSheepEncryptorBean;

    public static void main(String[] args) {
        SpringApplication.run(SpringBootConfigEncryptApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {

        Environment environment = appCtx.getBean(Environment.class);

        // Get the original plaintext information in the configuration file first
        String mysqlOriginPswd = environment.getProperty("spring.datasource.password");
        String redisOriginPswd = environment.getProperty("redis.password");
        String aliSmsOriginAk = environment.getProperty("ali.sms.access_key_secret");

        / / encryption
        String mysqlEncryptedPswd = encrypt( mysqlOriginPswd );
        String redisEncryptedPswd = encrypt( redisOriginPswd );
        String aliSmsEncryptedAk = encrypt( aliSmsOriginAk );

        // Prints the result comparison before and after encryption
        System.out.println( MySQL original plaintext password: + mysqlOriginPswd );
        System.out.println( "Redis original plaintext password:" + redisOriginPswd );
        System.out.println( "Ali cloud SMS original AccessKey password is:" + aliSmsOriginAk );
        System.out.println( "= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =" );
        System.out.println( MySQL original plaintext password encrypted result: + mysqlEncryptedPswd );
        System.out.println( "The result of Redis original plaintext password encryption is:" + redisEncryptedPswd );
        System.out.println( "The result of aliyun SMS original AccessKey password encryption is: + aliSmsEncryptedAk );
    }

    private String encrypt( String originPassord ) {
        String encryptStr = codeSheepEncryptorBean.encrypt( originPassord );
        return encryptStr;
    }

    private String decrypt( String encryptedPassword ) {
        String decryptStr = codeSheepEncryptorBean.decrypt( encryptedPassword );
        returndecryptStr; }}Copy the code

Run the project, console prints:

The original plaintext password of MySQL is:123456Redis original plaintext password:111111Ali cloud SMS for cryptographic primitives AccessKey: bImWdv13da894mly = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = MySQL original text passwords encrypted results as follows: IV7SyeQOfG4GhiXeGLboVgOLPDO + dJMDoOdmEOQp3KyVjruI + dKKeehsTriWPKbo Redis original text passwords encrypted results as follows: litUkxJ3fN6 +//Emq3vZ+y4o7ZOnZ8doOy7NrgJIDLoNWGG0m3ygGeQh/dEroKvvAli cloud SMS to the result of the original AccessKey passwords encrypted: MAhrOs20DY0RU/c1IKyLCt6dWZqLLOO4wUcK9GBgSxNII3C + y + SRptors + FyNz55xNDslhDnpWllhcYPwZsO5A = =Copy the code

5. Modify the configuration file and replace the configuration item to be encrypted

We get the encryption result of the previous step and replace the original plaintext password in the configuration file with the corresponding result of the previous step, like this:

So it is recommended that all important information in the wall split profile be treated this way!

6. View the decryption result

@SpringBootApplication
public class SpringBootConfigEncryptApplication implements CommandLineRunner {

    @Autowired
    private ApplicationContext appCtx;

    @Autowired
    private StringEncryptor codeSheepEncryptorBean;

    public static void main(String[] args) {
        SpringApplication.run(SpringBootConfigEncryptApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {

        Environment environment = appCtx.getBean(Environment.class);

        // Get the configuration items in the configuration file first
        String mysqlOriginPswd = environment.getProperty("spring.datasource.password");
        String redisOriginPswd = environment.getProperty("redis.password");
        String aliSmsOriginAk = environment.getProperty("ali.sms.access_key_secret");

        // Print the decrypted result
        System.out.println( MySQL original plaintext password: + mysqlOriginPswd );
        System.out.println( "Redis original plaintext password:" + redisOriginPswd );
        System.out.println( "Ali cloud SMS original AccessKey password is:"+ aliSmsOriginAk ); }}Copy the code

Print result:

The original password of MySQL in plain text is 123456. The original password of Redis in plain text is 111111. The original password of Alicloud SMS in plain text is bImWdv13da894mlyCopy the code

Obviously, when used in code, the jasypt-Spring-boot component automatically decrypts the configuration item encryption field wrapped in ENC() syntax and restores the data.


Do you have a lot of question marks?

At this time I think a lot of friends must be confused, typical such as:

1. The encryption key must be placed inENC()In the? Why is itENC?

2. Although the original configuration items related to information security are encrypted, the custom encryption key is usedjasypt.encryptor.password=CodeSheepIf leaked, others still have a chance to decrypt it?

For these questions, read on.


User-defined encryption prefix and suffix

If you don’t want to use jasypt’s default ENC to mark an encrypted field, you can use a custom prefix or suffix instead. For example, if I want to mark an encrypted field with CodeSheep(), I just need to configure the prefix or suffix in the configuration file:

jasypt.encryptor.property.prefix=CodeSheep(
jasypt.encryptor.property.suffix=)
Copy the code

The encrypted field can then be placed in the field marked by CodeSheep() :


Make encryption more secure

Although after the above encryption, information security configuration items will certainly become more secure, this is no doubt!

But if the custom in the configuration file encryption key jasypt. The encryptor. Password = CodeSheep revealed, that our encryption, decryption fields are likely to be others, therefore, there are several work can make encryption security.

1. Use custom encryptors

In the previous experiment, the default encryption rules were used, which may make it insecure when the custom encryption key is leaked. To do this, we can customize encryption rules.

Customizing encryption rules is as simple as providing a custom encryptor configuration class. For example, here I have a custom encryptorBean called codeSheepEncryptorBean:

@Configuration
public class CodeSheepEncryptorCfg {

    @Bean( name = "codeSheepEncryptorBean" )
    public StringEncryptor codesheepStringEncryptor(a) {

        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();

        SimpleStringPBEConfig config = new SimpleStringPBEConfig();
        config.setPassword("CodeSheep");
        config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
        config.setKeyObtentionIterations("1000");
        config.setPoolSize("1");
        config.setProviderName("SunJCE");
        config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
        config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
        config.setStringOutputType("base64");
        encryptor.setConfig(config);

        returnencryptor; }}Copy the code

Note that the Bean name name needs to be explicitly specified (the default name is jasyptStringEncryptor). If you use a custom name like here, You also need to specify the bean name in the Spring Boot application.properties configuration file, like this:

jasypt.encryptor.bean=codeSheepEncryptorBean
Copy the code

2. Do not write the encryption key in the configuration file

If the above method still causes the encryption key to leak (after all, it is written in the configuration file), we can simply remove the encryption key from the configuration file. There are three ways to do this:

  • Method 1: Enter it directly as a command line parameter when the program starts
java -jar yourproject.jar --jasypt.encryptor.password=CodeSheep
Copy the code
  • Method 2: Directly as the application environment variable when the program starts
java -Djasypt.encryptor.password=CodeSheep -jar yourproject.jar
Copy the code
  • Method 3: Can even be substituted as a system environment variable

JASYPT_ENCRYPTOR_PASSWORD = CodeSheep JASYPT_ENCRYPTOR_PASSWORD = CodeSheep JASYPT_ENCRYPTOR_PASSWORD = CodeSheep JASYPT_ENCRYPTOR_PASSWORD = CodeSheep

jasypt.encryptor.password=${JASYPT_ENCRYPTOR_PASSWORD:}
Copy the code

It’s also a lot safer.


SHH…

Ok, said so much, if your project configuration file in the important information is not encrypted, promise me, say no more words, quickly all secretly to change, quick! Speed! Run ahead!