Mybatis study notes -11 value symbols $, # and SQL injection problems

Difference 1:

When @Parm("xx") indicates the parameter name, for numeric data, there is no substantial difference between # and $. For character data, the value retrieved by $ will not have its own quotation marks''.

Numerical experiment:

1. Add method to DAO

User queryUserById(@Param("id") Integer id);

2. Write Mapper.xml

 A. Use # for value

    <sql id="user_field">
        select id,username,password,gender,regist_time as registerTime
        from t_user
    </sql>
    <select id="queryUserById" resultType="User">
        <include refid="user_field"></include>
        where id = #{id}
    </select>

B. Take the value of $

    <sql id="user_field">
        select id,username,password,gender,regist_time as registerTime
        from t_user
    </sql>
    <select id="queryUserById" resultType="User">
        <include refid="user_field"></include>
        where id = #{id}
    </select>

[note! ! ! ]: Through the SQL statement output from the console, we can also find:

This is #output SQL;

 This is the SQL output by $;

$ Does direct string splicing, while # is obviously not. It uses placeholders. It will choose whether to add '' according to whether it is a character type.

Character experiment:

1. Add method to DAO

User queryUserByUsername(@Param("username") String username);

2. Write Mapper.xml

 A. Use # for value

    <select id="queryUserByUsername" resultType="User">
        <include refid="user_field"></include>
        where username = #{username}
    </select>

B. Take the value of $

    <select id="queryUserByUsername" resultType="User">
        <include refid="user_field"></include>
        where username = ${username}
    </select>

Correct writing:

    <select id="queryUserByUsername" resultType="User">
        <include refid="user_field"></include>
        where username = '${username}'
    </select>

Difference 2:

When the @Param annotation is not used, # supports arg0, arg1 or param1, param2 for scattered parameters, but $ does not support. When using @Param, both # and $ support param.

A. @Param Note+ #{param1}

    <select id="queryUserById" resultType="User">
        <include refid="user_field"></include>
        where id = #{param1}
    </select>

B. @Param Comment + ${param}

    <select id="queryUserById" resultType="User">
        <include refid="user_field"></include>
        where id = ${param1}
    </select>

The difference is still the difference in SQL statements. If readers still have an impression, the main function of PreparedStatement is to prevent SQL injection, which will be discussed later.

Note: If the parameter uses @Param annotation, # only supports param but not arg.

C. Remove @Param annotation

1) #{arg0} or #{param1}

User queryUserById(Integer id);
    <select id="queryUserById" resultType="User">
        <include refid="user_field"></include>
        where id = #{arg0}
    </select>
    <select id="queryUserById" resultType="User">
        <include refid="user_field"></include>
        where id = #{param1}
    </select>

2) ${arg0} or ${param1}

    <select id="queryUserById" resultType="User">
        <include refid="user_field"></include>
        where id = ${arg0}
    </select>

From the error message, we can see that after removing the @Param annotation, ${arg0} actually treats arg0 as an attribute named arg0 of the class parameter Integer id, trying to get the value through the get method.

    <select id="queryUserById" resultType="User">
        <include refid="user_field"></include>
        where id = ${param1}
    </select>

From the error message, we can see that after removing the @Param annotation, ${param1} actually treats param1 as an attribute named param1 of the class parameter Integer id, trying to get the value through the get method.

It is worth noting: In Mybatis, even if the parameters of the method we set in the DAO interface are basic data types, when Mybatis is generated, the parameters will still be changed to packaging classes of basic data types, such as:

User queryUserById(int id);
    <select id="queryUserById" resultType="User">
        <include refid="user_field"></include>
        where id = ${id}
    </select>

[Summary]: If you must use $, then the scattered parameters must be annotated. For characters or strings, you need to manually add quotation marks.

Difference 3 [important]:

 SQL injection means that we can use SQL statements to circumvent the security checks that only use SQL statements. For example, when we want to log in to the system, we usually verify the user name or password.

The normal SQL statement we want to execute: "select * from t_user where username = '"+ username +"' and password = '"+ password +"' ".

But if we assign the password to '123456 or 1 = 1', the executed SQL statement will become:

select * from t_user where username = '"+ username +"' and 123456 or 1 = 1 '", since 1=1 is the condition, this statement can find out all the data in the table, which will completely bypass Open the security check with SQL.

Let's make an example:

List<User> Login(@Param("username") String id,@Param("password") String password);
    <select id="Login" resultType="User">
        select * from t_user
        where username = '${username}' and password = '${password}'
    </select>
    @Test
    public void testLogin(){
        List<User> zzt = mapper.Login("zzt", "111' or '1' = '1");
        for (User user : zzt) {
            System.out.println(zzt);
        }
    }

Careful readers may also have discovered that the premise of this is that the return of the entire method must be a List. If it is an entity class type, it will cause errors due to too many objects to be encapsulated.

But this does not mean that we don’t need to worry about SQL injection. If the login logic of the entire system is, as long as there is more than one piece of system data found, it can be released, especially for some accounts and passwords that are allowed to be repeated. That is, use other values ​​as the primary key.

In addition, let's talk about a more possible situation. We know that a large number of database query operations are quite time-consuming. Through SQL injection, we can query all data. Then as long as the client continues to inject SQL into the server, the server A large number of queries may cause cache exhaustion, affect system response, or even crash.

 So why can #use placeholders avoid the problem of SQL injection? We can see the SQL statement at a glance:

In fact, the translation is: select * from t_user where username ='zzt' and password = "111' or '1' = '1 ", that is, 111' or '1' = '1 is used as a password, Naturally prevents injection

but! but! but! In some cases, placeholders cannot be used, such as allowing data to be searched and output in an orderly manner according to dynamic rules: select * from t_user order by id desc;

List<User> queryByDesc(@Param("order") String order);
    <select id="queryByDesc" resultType="User">
        select * from t_user order by id #{order}
    </select>
    @Test
    public void testQueryByDesc(){
        List<User> users = mapper.queryByDesc("desc");
        for (User user : users) {
            System.out.println(user);
        }
    }

Obviously: select * from t_user order by id'desc'; is a syntax error.

Use splicing:

    <select id="queryByDesc" resultType="User">
        select * from t_user order by id ${order}
    </select>

In order to ensure safety and prevent injection, we do not allow users to directly input rules, only the parameters corresponding to the rules:

    @Test
    public void testQueryByDesc(){
        int signal = 0;
        List<User> users;
        if( signal == 0 ){
            users = mapper.queryByDesc("desc");
        }else{
            users = mapper.queryByDesc("asc");
        }
        for (User user : users) {
            System.out.println(user);
        }
    }

[Summary]: When the data to be filled is related to the column attributes, using placeholders can indeed avoid the risk of SQL injection. If used in other situations (related to SQL itself), it is very likely to cause SQL syntax errors. SQL splicing will have injection risks, but as long as the syntax is correct, there are no restrictions (the restrictions here refer to the use rather than the prevention of security issues caused by injection). In most cases, we still recommend using #.

Guess you like

Origin blog.csdn.net/qq_39304630/article/details/112305787