Writing in the front

In an article in the introduction to Shiro learning – using custom Realm completed certification practice mid air | “, we learn to use a custom Realm implementations Shiro data source switch, we can switch to reads the user authentication information from a relational database such as MySQL authentication, User authentication information can also be read from a non-relational database such as mongodb for authentication. This is great progress, and it allows us to use Shiro to improve the security of our applications,

So, let’s ask ourselves, are our applications really secure?

I put our last article in the authentication method code excerpted below for you to see

/**认证
 * @authorLaifeng [email protected] *@dateThe 2020-10-04 11:01:50 *@param authenticationToken
 * @return org.apache.shiro.authz.AuthorizationInfo
 * @throws AuthenticationException
 * @version1.0 * /
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    // 1. Obtain the user name from the token
    String principal = (String) authenticationToken.getPrincipal();

    //2. Query database based on user name and encapsulate as authenticationInfo object return (mock)
    if (principal == "xiangbei") {
        AuthenticationInfo authInfo = new SimpleAuthenticationInfo("xiangbei"."123".this.getName());
        return authInfo;
    }

    return null;
}
Copy the code

In line 16, we simulate querying the user’s registration information from the database, including the account and password, and the password here is in plain text. This means that if our user passwords are compromised (except for user-induced leaks), some unfriendly people will be able to get in and out of our system at will. Not only does this make our application insecure, but it also exposes us to legal risk.

The following is excerpted from the Cyber Security Law

Article 34 Network operators shall establish and improve the user information protection system and strengthen the protection of users’ personal information, privacy and business secrets.

Article 35 When collecting and using citizens’ personal information, network operators shall follow the principles of legality, legitimacy and necessity, state clearly the purpose, method and scope of the collection and use of information, and obtain the consent of the people to be collected. Network operators shall not collect citizens’ personal information irrelevant to the services they provide, and shall not collect or use citizens’ personal information in violation of the provisions of laws and administrative regulations and agreements between the two parties, and shall dispose of the citizens’ personal information they keep in accordance with the provisions of laws and administrative regulations or agreements with users. Network operators shall disclose the collection and use rules of citizens’ personal information.

Article 36 Network operators must strictly keep the personal information they have collected confidential, and may not disclose, tamper with, damage or sell it or illegally provide it to others. ** Network operators shall take technical measures and other necessary measures to ensure the security of citizens’ personal information and prevent the disclosure, destruction, damage and loss of citizens’ personal information collected by them. ** In the event of information disclosure, damage or loss occurring or likely to occur, it shall immediately take remedial measures, inform the users who may be affected and report to the relevant competent authorities in accordance with regulations.

Therefore, we need to encrypt and protect user information. For account password information, we should use irreversible encryption. In other words, after we encrypt the password and store it, even if it has obtained our ciphertext, it cannot get our password in plain text. This has a very good protection effect on our user information.

MD5 encryption algorithm and salt encryption

MD5 encryption algorithm

What is MD5 encryption

MD5 message-digest Algorithm (MD5 message-digest Algorithm) is a widely used password hash function that produces a 128-bit (16-byte) hash value to ensure that information is transmitted consistently.

The characteristics of

  1. Irreversibility, that is, it cannot itself be derived from ciphertext to clear text,

    However, if the plaintext is relatively simple and common, there is still the risk of disclosure, such as Sir Into a good simple plaintext ciphertext, and then use the exhaustive method to crack;

  2. For the same plaintext, the ciphertext is the same no matter how many times it is encrypted.

  3. The result is always a hexadecimal 32-bit string.

role

  1. Digital signature (checksum)

    For example, in order to ensure that a file does not change during network transmission, I use MD5 encryption algorithm to encrypt it in advance and obtain a ciphertext. I’m sending you this document and the cipher text separately. You also use MD5 to encrypt the file once you receive it and get a ciphertext. In this case, you can compare the two ciphertexts. If they are consistent, the files are not tampered with. Otherwise, the files have been tampered with.

  2. encryption

  3. Spam filtering

    Same principle as action 1

Salt Indicates the salt value encryption policy

In the introduction of MD5 encryption algorithm above, we said that although MD5 algorithm itself is irreversible, if the user uses a simple string as the password, there is still a risk of brute force cracking. Therefore, to solve this problem, we need to complicate the password before encrypting it.

And adding salt is one way to do that. Salting is simply adding a random string to the original password. And then encrypt it.

Of course, if the salt value is leaked along with the password, there is a risk that the password will be cracked, so we can only be relatively safe.

In order to increase the difficulty of cracking, certain strategies can be adopted when adding salt, such as hashing salt and hashing multiple times after encryption.

Of course, this requires a direct balance between security and performance.

Shiro uses MD5+salt encryption

Analysis of the

Before coding, we need to clarify the flow:

  1. When a user registers or the system allocates an account, the service layer encrypts the credential information with MD5 + SALT after receiving the account and credential information, and then stores the account, encrypted password and salt value in the database.

  2. After receiving the login request, the user queries the database according to the account in the request:

    2.1 If no, a similar message “The user name or password is incorrect” is displayed

    2.2 If you find the account information, go to Step 3.

  3. Encapsulate the account and salted credentials as an AuthenticationInfo object and return it to Shiro. Shiro goes to Step 4

  4. Salt the credentials in the request and perform Step 5

  5. Md5 encryption is performed on the salted certificate, and the ciphertext is compared with that stored in the database:

    5.1 If the match is successful, the authentication succeeds

    5.2 If the match fails, a similar message “The username or password is incorrect” is displayed

implementation

Write a custom Realm and switch the default credential matcher

/** Customize Realm objects *@authorLaifeng [email protected] *@version 1.0
 * @date2020/10/4 11 * /
public class MySqlRealm extends AuthorizingRealm {

    public MySqlRealm(a) {
        // Set the credential matcher to hash
        HashedCredentialsMatcher myCredentialsMatcher = new HashedCredentialsMatcher();
        // Set the algorithm
        myCredentialsMatcher.setHashAlgorithmName("md5");
        // Hash times
        myCredentialsMatcher.setHashIterations(1024);
        this.setCredentialsMatcher(myCredentialsMatcher);
    }

    / authorization * * *@authorLaifeng [email protected] *@dateThe 2020-10-04 11:01:50 *@param principalCollection
     * @return org.apache.shiro.authz.AuthorizationInfo
     * @throws AuthenticationException
     * @version1.0 * /
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        return null;
    }

    /**认证
     * @authorLaifeng [email protected] *@dateThe 2020-10-04 11:01:50 *@param authenticationToken
     * @return org.apache.shiro.authz.AuthorizationInfo
     * @throws AuthenticationException
     * @version1.0 * /
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 1. Obtain the user name from the token
        String principal = (String) authenticationToken.getPrincipal();

        //2. Query database based on user name and encapsulate as authenticationInfo object return (mock)
        if (principal == "xiangbei") {
            // Four parameters are the database account, encrypted password, salt value, and realm name
            AuthenticationInfo authInfo = new SimpleAuthenticationInfo("xiangbei"."ff595c47b51b4cf70fddce090f68879e",
                    ByteSource.Util.bytes("ee575f62-0dda-44f2-b75e-4efef795018f"),
                    this.getName());
            return authInfo;
        }

        return null; }}Copy the code

Write authenticator

/** Authentication manager *@authorLaifeng [email protected] *@version 1.0
 * @date2020/10/4 11:11 * /
public class CurrentSystemAuthenticator {
    private DefaultSecurityManager securityManager;
    public CurrentSystemAuthenticator(a) {
        // Create security manager
        securityManager = new DefaultSecurityManager();

        // Set a custom realm
        this.securityManager.setRealm(new MySqlRealm());

        // Set the security manager to the security utility class
        SecurityUtils.setSecurityManager(securityManager);

    }

    public void authenticate(String username,String password) {
        // Get the current login topic
        Subject subject = SecurityUtils.getSubject();

        / / generated toeken
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        // Perform authentication
        try {
            subject.login(token);
        }catch (UnknownAccountException | IncorrectCredentialsException e) {
            System.out.println("Incorrect username or password");
        }


        // Prints the authentication status
        if (subject.isAuthenticated()){
            System.out.println(token.getPrincipal()+"Approved!");
        }else {
            System.out.println(token.getPrincipal()+"Certification failed!"); }}}Copy the code

test

Generate an encrypted password
/ * * *@authorLaifeng [email protected] *@version 1.0
 * @date2020/10/4 21:37 * /
public class Md5Test {

    @Test
    public void testMd5(a){
        // The three parameters correspond to the plain password, salt value, and hash times respectively
        String salt = UUID.randomUUID().toString();
        Md5Hash md5Hash = new Md5Hash("123", salt,1024);
        System.out.println("Ciphertext:"+md5Hash.toHex());
        System.out.println("Salt value:"+salt); }}Copy the code

The output

Cipher: ff595c47b51b4cf70fddce090f68879e salt value: ee575f62 0 efef795018f dda - 44 f2 - b75e - 4Copy the code
Conduct certification tests
/ * * *@authorLaifeng [email protected] *@version 1.0
 * @dateThe loathsome 2020/10/4 * /
public class AuthcTest {
    private CurrentSystemAuthenticator authenticator;
    @Before
    public void init(a) {
        this.authenticator = new CurrentSystemAuthenticator();
    }

    @Test
    public void testAuthc(a){
        this.authenticator.authenticate("xiangbei"."123"); }}Copy the code

The output

Xiangbei certified!Copy the code

Write in the last

In this article, we took a brief look at the encryption strategies in Shiro and how to encrypt passwords using MD5+salt. Try swapping MD5 for SHA-256.

In the next article, the author will cover the integration of Shiro with SpringBoot. This article may be a bit long and will be written in two installments. Please pay more attention.

Welcome to like, retweet and share. Reprint to indicate the source with the original link.