Everybody is good! I’m xiao Fu

These days, the company is investigating the leak of internal data account. The reason is that some interns have been found to upload the source code to GitHub privately with their account and password, resulting in the leakage of core data. The child has not been beaten by the society.

Before talking about this is I have a feeling, my experience of TM was delete library, now to think of it in the mind also uncomfortable, I also am the plaintext password mistakenly submitted to making database account, which is then big baby give me test library is deleted, I have a long memory behind the configuration file content encryption, data security problem to be reckoned with, life or job transfer, Sensitive data must be desensitized.

If you are not familiar with the concept of desensitization, you can take a look at a big factory before I have written a 6 data desensitization scheme, which has done a simple description of desensitization, and then share two more common desensitization scenarios in the work.

Configuration desensitization

Desensitization of configuration I used a Java encryption and decryption tool Jasypt, it provides a single key symmetric encryption and asymmetric encryption of two desensitization.

Single-key symmetric encryption: a key with salt can be used as the basis for both encryption and decryption of content;

Asymmetric encryption: The public key and private key are used to encrypt and decrypt the contents.

The above two encryption methods are very simple to use, let’s take springBoot integrated single key symmetric encryption as an example.

First, introduce the Jasypt-spring-boot-starter JAR

 <! -- Config file encryption -->
 <dependency>
     <groupId>com.github.ulisesbocchio</groupId>
     <artifactId>jasypt-spring-boot-starter</artifactId>
     <version>2.1.0</version>
 </dependency>
Copy the code

Configuration files to the secret key configuration items jasypt. The encryptor, password, and it will need to be the value of desensitization value to replace the previously encrypted content ENC (mVTvp4IddqdaYGqPl9lCQbzM3H/b0B6l).

We can be freely defined the format, such as to ABC mVTvp4IddqdaYGqPl9lCQbzM3H/b0B6l format, as long as the configuration prefixes and suffixes.

jasypt:
  encryptor:
    property:
      prefix: "abc["
      suffix: "]"
Copy the code

The ENC(XXX) format is mainly used to identify whether the value needs to be decrypted. If not configured according to this format, jasypt will keep the original value and not decrypt when loading the configuration item.

spring:
  datasource:
    url: jdbc:mysql:/ / 2:3306 / xiaofu? useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&ze oDateTimeBehavior=convertToNull&serverTimezone=Asia/ShanghaiUsername: xiaofu password: ENC (mVTvp4IddqdaYGqPl9lCQbzM3H/b0B6l) # secret-key jasypt: the encryptor: password: programmers in something (but does not support Chinese)Copy the code

The secret key is an attribute requiring high security, so it is generally not recommended to put it directly in the project. You can inject the -d parameter at startup or put it in the configuration center to avoid leakage.

java -jar -Djasypt.encryptor.password=1123  springboot-jasypt-2.33..RELEASE.jar
Copy the code

Pre-generated encrypted values that can be generated by calling the API in code

@Autowired
private StringEncryptor stringEncryptor;

public void encrypt(String content) {
    String encryptStr = stringEncryptor.encrypt(content);
    System.out.println("Encrypted content:" + encryptStr);
}
Copy the code

Jar :\maven_lib\org\jasypt\jasypt\1.9.3\jasypt-1.9.3.jar: jasypt core jar, input text to be encrypted, password secret key, Algorithm indicates the encryption algorithm used.

java -cp  D:\maven_lib\org\jasypt\jasypt\1.93.\jasypt-1.93..jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="root" password=xiaofu  algorithm=PBEWithMD5AndDES
Copy the code

If the operation can be started normally, the configuration file desensitization is no problem.

Desensitization of sensitive fields

The privacy data of users in production environment, such as mobile phone number, ID card or some account configuration information, should be desensitized without landing when entering the storage, that is, real-time desensitization processing should be carried out when entering our system.

User data into the system, desensitization processing persisted to the database, the user query data but also reverse decryption. This type of scenario generally requires global processing, so an AOP aspect is a good fit.

If the @encryptMethod annotation is applied to the method, check whether the field is annotated with @encryptField. If the @encryptMethod annotation is applied to the method, check whether the field is annotated with @encryptField. If yes, the corresponding fields are encrypted.

@Documented
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {

    String[] value() default "";
}
Copy the code
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptMethod {

    String type(a) default ENCRYPT;
}
Copy the code

The implementation of the aspect is also relatively simple, to enter the secret, return the result of decryption. For easy reading, only part of the code is posted here, the full case is Github: github.com/chengxy-nds…

@Slf4j
@Aspect
@Component
public class EncryptHandler {

    @Autowired
    private StringEncryptor stringEncryptor;

    @Pointcut("@annotation(com.xiaofu.annotation.EncryptMethod)")
    public void pointCut(a) {}@Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        /** * Encryption */
        encrypt(joinPoint);
        /** * Decrypt */
        Object decrypt = decrypt(joinPoint);
        return decrypt;
    }

    public void encrypt(ProceedingJoinPoint joinPoint) {

        try {
            Object[] objects = joinPoint.getArgs();
            if(objects.length ! =0) {
                for (Object o : objects) {
                    if (o instanceof String) {
                        encryptValue(o);
                    } else {
                        handler(o, ENCRYPT);
                    }
                    // The rest of the TODO types are added according to the actual situation}}}catch(IllegalAccessException e) { e.printStackTrace(); }}public Object decrypt(ProceedingJoinPoint joinPoint) {
        Object result = null;
        try {
            Object obj = joinPoint.proceed();
            if(obj ! =null) {
                if (obj instanceof String) {
                    decryptValue(obj);
                } else {
                    result = handler(obj, DECRYPT);
                }
                // The rest of the TODO types are added according to the actual situation}}catch (Throwable e) {
            e.printStackTrace();
        }
        returnresult; }... }Copy the code

Next, to test the effect of the aspect annotations, we desensitize the fields mobile and Address with the @EncryptField annotation.

@EncryptMethod
@PostMapping(value = "test")
@ResponseBody
public Object testEncrypt(@RequestBody UserVo user, @EncryptField String name) {

    return insertUser(user, name);
}

private UserVo insertUser(UserVo user, String name) {
    System.out.println("Encrypted data: user" + JSON.toJSONString(user));
    return user;
}

@Data
public class UserVo implements Serializable {

    private Long userId;

    @EncryptField
    private String mobile;

    @EncryptField
    private String address;

    private String age;
}
Copy the code

Request this interface, see that the parameters are successfully encrypted, and the data returned to the user is still the data before desensitization, in line with our expectations, then this simple desensitization implementation is done.

You know what you’re doing

Although Jasypt tool is simple and easy to use, as programmers, we should not only be satisfied with skilled use. It is necessary to understand the underlying implementation principle, which is very important for subsequent bug debugging and secondary development and extension functions.

In my opinion, the principle of Jasypt profile desensitization is very simple. It is no more than intercepting the operation of obtaining the configuration information and decrypting the corresponding encryption configuration before using the specific configuration information.

Specific is not so we simply look at the source code implementation, since it is the SpringBoot way integration, then start from jasypt-Spring-boot-starter source.

Starter code is small, the main work is through the SPI mechanism registration services and @ Import annotations to inject JasyptSpringBootAutoConfiguration pre-processing of the class.

Registered in front loading class EnableEncryptablePropertiesConfiguration a EnableEncryptablePropertiesBeanFactoryPostProcessor core processing class.

Its constructor takes two parameters, ConfigurableEnvironment used to retrieve information, all attachment EncryptablePropertySourceConverter do resolution with configuration information.

Ultimately found to be responsible for decrypting the processing class EncryptablePropertySourceWrapper, it through to the Spring property management class PropertySource < T > do develop, rewrite the getProperty (String name) method, When obtaining the configuration, all values in the specified format such as ENC(x) packages are decrypted.

Now that we know the principle of the subsequent secondary development, such as: switch encryption algorithm or achieve their own desensitization tool is much easier.

Github: github.com/chengxy-nds…

PBE algorithm

Let’s talk about the encryption algorithm Jasypt uses. In fact, it is encapsulated on the basis of JDK jCE. jar package. Essentially, it uses the algorithm provided by JDK. PBE, WITH, MD5, AND DES

The Password Based Encryption (PBE) algorithm is a Password Based Encryption algorithm. Its characteristic is that the Password is mastered by the user, and multiple Encryption methods such as adding random numbers are used to ensure data security.

The PBE algorithm doesn’t really build new encryption and decryption algorithms, but wraps up the ones we already know. For example, the common message digest algorithms MD5 and SHA, symmetric encryption algorithms DES and RC2, and PBE algorithm is a reasonable combination of these algorithms, which also echoes the name of the previous algorithm.

Since the PBE algorithm uses our more common symmetric encryption algorithm, there is a key issue. But it itself has no key concept, only password password, the key is the password through encryption algorithm calculation.

The password itself isn’t long enough to be used in place of a key, and it’s easy to crack it with an exhaustive attack, which requires a little salt.

Salt is usually random information, such as a random number or a time stamp, which is added to the password to make it more difficult to decipher.

Source code in the cat

Simple understanding of the PBE algorithm, back to Jasypt source code is how to achieve encryption and decryption.

At the time of encryption first instantiation SecretKeyFactory secret key factory, produce eight values, salt. The default use jasypt encryptor. RandomSaltGenerator generator.

public byte[] encrypt(byte[] message) {
    // Initializes the key factory according to the specified algorithm
    final SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm1);
    // Salt value generator, select only 8 bits
    byte[] salt = saltGenerator.generateSalt(8);
    // 
    final PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iterations);
    // Generate the secret key with the salt value and password
    SecretKey key = factory.generateSecret(keySpec);

    // Build an encryptor
    final Cipher cipherEncrypt = Cipher.getInstance(algorithm1);
    cipherEncrypt.init(Cipher.ENCRYPT_MODE, key);
    // Ciphertext header (salt value)
    byte[] params = cipherEncrypt.getParameters().getEncoded();

    // Call the underlying implementation of encryption
    byte[] encryptedMessage = cipherEncrypt.doFinal(message);

    // Assemble the final ciphertext content and allocate memory (salt value + ciphertext)
    return ByteBuffer
            .allocate(1 + params.length + encryptedMessage.length)
            .put((byte) params.length)
            .put(params)
            .put(encryptedMessage)
            .array();
}
Copy the code

Because of the default use of a random salt value generator, the same content is different each time it is encrypted.

So what do you do when you decrypt it?

Looking at the source code above, we find that the final encrypted text consists of two parts: the Params message header contains the password and the randomly generated salt value, and the encryptedMessage.

When decrypting, the contents of the encryptedMessage will be disassembled from the params to parse out the salt value and password, and the underlying JDK algorithm will be called to decrypt the actual content.

@Override
@SneakyThrows
public byte[] decrypt(byte[] encryptedMessage) {
    // Get the ciphertext header
    int paramsLength = Byte.toUnsignedInt(encryptedMessage[0]);
    // Get the ciphertext content
    int messageLength = encryptedMessage.length - paramsLength - 1;
    byte[] params = new byte[paramsLength];
    byte[] message = new byte[messageLength];
    System.arraycopy(encryptedMessage, 1, params, 0, paramsLength);
    System.arraycopy(encryptedMessage, paramsLength + 1, message, 0, messageLength);

    // Initialize the secret key factory
    final SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm1);
    final PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
    SecretKey key = factory.generateSecret(keySpec);

    // Construct the header password parameter with a salt value
    AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance(algorithm1);
    algorithmParameters.init(params);

    // Build an encryptor and invoke the underlying algorithm
    final Cipher cipherDecrypt = Cipher.getInstance(algorithm1);
    cipherDecrypt.init(
            Cipher.DECRYPT_MODE,
            key,
            algorithmParameters
    );
    return cipherDecrypt.doFinal(message);
}
Copy the code

I’m rich. See you next time

Sorted out hundreds of various types of technical e-books, students in need of their own. Technology group is almost full, want to enter the students can add my friend, and the big guys to blow technology.

Ebook address

Personal public number: programmers inside the matter, welcome to communicate