【Network Security】Detailed Explanation of SQL Injection

1. What is sql injection

SQL injection is one of the more common network attack methods. It does not use the BUG of the operating system to realize the attack, but targets at the negligence of programmers when writing, and realizes login without account through SQL statements, and even tampers with the database.

 2. The general idea of ​​SQL injection attack 

  1: Find the location of SQL injection

  2: Determine the server type and background database type

  3: SQL injection attacks against different server and database characteristics

 3. Examples of SQL injection attacks

String sql = "select * from user_table where username=
' "+userName+" ' and password=' "+password+" '";

--当输入了上面的用户名和密码,上面的SQL语句变成:
SELECT * FROM user_table WHERE username=
'’or 1 = 1 -- and password='’

"""
--分析SQL语句:
--条件后面username=”or 1=1 用户名等于 ” 或1=1 那么这个条件一定会成功;

--然后后面加两个-,这意味着注释,它将后面的语句注释,让他们不起作用,这样语句永远都--能正确执行,用户轻易骗过系统,获取合法身份。
--这还是比较温柔的,如果是执行
SELECT * FROM user_table WHERE
username='' ;DROP DATABASE (DB Name) --' and password=''
--其后果可想而知…
"""

4. How to defend against SQL injection

Note: Any program with a SQL injection vulnerability is because the program accepts a variable input by the client user or a parameter passed by the URL, and this variable or parameter is part of the SQL statement. For the content entered by the user or the parameter passed , we should always be vigilant. This is the principle of "external data cannot be trusted" in the security field. Looking at various attack methods in the web security field, most of them are caused by developers violating this principle, so What can naturally be thought of is to start with the detection, filtering, and verification of variables to ensure that the variables are what the developer expected.

1. Check the variable data type and format

If your SQL statement is in the form of where id={$id}, and all ids in the database are numbers, then you should check to ensure that the variable id is of type int before the SQL is executed; if it accepts mailboxes, then You should check and strictly ensure that the variable must be in the format of the mailbox. Other types such as date and time are also the same. To sum up: As long as there is a variable with a fixed format, before the SQL statement is executed, it should be checked strictly according to the fixed format to ensure that the variable is in the format we expected, so that SQL injection attacks can be avoided to a large extent.
For example, in our previous example of accepting the username parameter, our product design should have a username rule at the beginning of user registration, such as 5-20 characters, which can only be composed of uppercase and lowercase letters, numbers and some safe characters. Symbols, no special characters. At this point we should have a check_username function to perform a unified check. However, there are still many exceptions that cannot be applied to this rule, such as article publishing systems, comment systems, and other scenarios that must allow users to submit arbitrary strings, which requires other solutions such as filtering.

2. Filter special symbols

For variables whose fixed format cannot be determined, special symbol filtering or escaping must be performed.

3. Bind variables, use precompiled statements  

MySQL's mysqli driver provides support for precompiled statements. Different programming languages ​​have methods for using precompiled statements.

In fact, using precompiled statements for bind variables is the best way to prevent SQL injection. The semantics of precompiled SQL statements will not change. In SQL statements, variables are represented by question marks?, and hackers, no matter how powerful they are, cannot Change the structure of the SQL statement

5. What is sql precompilation

1.1: What is a prepared statement 

Usually our sql receives the final execution and returns in the db can be divided into the following three processes:

  1. Lexical and Semantic Analysis
  2. Optimize sql statement and make execution plan
  3. Execute and return the result

We call this common statement Immediate Statements.  

But in many cases, one of our SQL statements may be executed repeatedly, or only individual values ​​are different each time it is executed (for example, the value of the where clause of the query is different, the value of the set clause of the update is different, and the values ​​​​of the insert are different).
If you need to go through the above lexical and semantic analysis, statement optimization, execution plan formulation, etc. every time, the efficiency will obviously not work.

The so-called precompiled statement is to replace the value in this type of statement with a placeholder, which can be regarded as templated or parameterized SQL statement. Generally, this type of statement is called Prepared Statements or Parameterized Statements. The advantage of precompiled statements is that they can be summarized
as : Compile once and run multiple times, eliminating the need for parsing and optimization; in addition, precompiled statements can prevent SQL injection.
Of course, in terms of optimization, in many cases, the optimal execution plan cannot be determined just by knowing the template of the SQL statement, but often needs to estimate the cost through specific values.

1.2: MySQL precompilation function

Note that the old version of MySQL (before 4.1) does not support server-side precompilation, but based on the current general situation in the industry's production environment, it can basically be considered that MySQL supports server-side precompilation.

Let's take a look at the use of prepared statements in MySQL.
(1) Building a table First, we have a test table t, the structure of which is as follows:

mysql> show create table t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `a` int(11) DEFAULT NULL,
  `b` varchar(20) DEFAULT NULL,
  UNIQUE KEY `ab` (`a`,`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

(2) compile

We  PREPARE stmt_name FROM preparable_stmwill precompile a sql statement with the syntax we will pass next

mysql> prepare ins from 'insert into t select ?,?';
Query OK, 0 rows affected (0.00 sec)
Statement prepared

(3) Execution

The syntax we pass EXECUTE stmt_name [USING @var_name [, @var_name] ...]to execute prepared statements

mysql> set @a=999,@b='hello';
Query OK, 0 rows affected (0.00 sec)
 
mysql> execute ins using @a,@b;
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0
 
mysql> select * from t;
+------+-------+
| a    | b     |
+------+-------+
|  999 | hello |
+------+-------+
1 row in set (0.00 sec)

As you can see, the data has been successfully inserted into the table.

The scope of precompiled statements in MySQL is session level, but we can use the max_prepared_stmt_count variable to control the global maximum stored precompiled statements.

mysql> set @@global.max_prepared_stmt_count=1;
Query OK, 0 rows affected (0.00 sec)
 
mysql> prepare sel from 'select * from t';
ERROR 1461 (42000): Can't create more than max_prepared_stmt_count statements (current value: 1)

When the number of precompiled entries has reached the threshold, you can see that MySQL will report the error shown above.

(4) Release
If we want to release a precompiled statement, we can use {DEALLOCATE | DROP} PREPARE stmt_namethe syntax to do so:

mysql> deallocate prepare ins;
Query OK, 0 rows affected (0.00 sec)

6. Why PrepareStatement can prevent SQL injection

The principle is to use the pre-compilation method, first compile the parameter set in the SQL statement that can be controlled by the client, generate the corresponding temporary variable set, and then use the corresponding setting method to assign values ​​to the elements in the temporary variable set. The function setString() will perform mandatory type checking and security checking on the incoming parameters, so SQL injection can be avoided. The following specific analysis

(1): Why Statement will be injected by sql

Because Statement is injected by SQL because the SQL statement structure has changed. for example:

"select*from tablename where username='"+uesrname+  
"'and password='"+password+"'"

The sql statement structure changes after the user enters 'or true or'.

select*from tablename where username=''or true or'' and password=''

In this way, it is only counted when the user name and password match, but after the change, it becomes an OR logic relationship, regardless of whether the user name and password match or not, the return value of this formula is always true;

(2) Why Preparement can prevent SQL injection.

Because the Preparement style is

select*from tablename where username=? and password=?

The SQL statement will be pre-compiled with the database before getting the user's input. In this way, no matter what user name and password the user enters, the judgment is always a logical relationship, preventing SQL injection.

In a brief summary, the reason why parameterization can prevent injection is that a statement is a statement, a parameter is a parameter, and the value of a parameter is not part of the statement. The database only runs according to the semantics of the statement. As for whether to run with a normal backpack or a monster, It will not affect the route of travel, it is nothing more than the difference between running faster and slower.

7. How does mybatis prevent SQL injection  

1. First look at the difference between the following two SQL statements:

<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
select id, username, password, role
from user
where username = #{username,jdbcType=VARCHAR}
and password = #{password,jdbcType=VARCHAR}
</select>
<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
select id, username, password, role
from user
where username = ${username,jdbcType=VARCHAR}
and password = ${password,jdbcType=VARCHAR}
</select>

The difference between # and $ in mybatis:

1. # Treat the incoming data as a string, and add double quotation marks to the automatically incoming data.
For example: where username=#{username}, if the value passed in is 111, then the value when parsed into sql is where username="111", if the value passed in is id, then the sql parsed into is where username= "id". 

2. $ directly display and generate the incoming data in sql.
For example: where username=${username}, if the value passed in is 111, then the value when parsed into sql is where username=111;
if the value passed in is ;drop table user;, then the parsed sql is: select id, username, password, role from user where username=;drop table user;

3. The # method can prevent SQL injection to a large extent, but the $ method cannot prevent Sql injection.
4. The $ method is generally used to import database objects, such as the table name.
5. Do not use $ if you can generally use #. If you have to use parameters such as "${xxx}", you must manually filter them Work, to prevent sql injection attacks.
6. In MyBatis, parameters in the format of "${xxx}" will directly participate in SQL compilation, so injection attacks cannot be avoided. But when it comes to dynamic table names and column names, only the parameter format "${xxx}" can be used. Therefore, such parameters need to be manually processed in the code to prevent injection.

[Conclusion] When writing the mapping statement of MyBatis, try to use the format "#{xxx}". If you have to use parameters such as "${xxx}", you must manually filter them to prevent SQL injection attacks.

How does mybatis prevent sql injection

As a semi-automated persistence layer framework, the MyBatis framework requires us to manually write its SQL statements. Of course, it is necessary to prevent SQL injection at this time. In fact, MyBatis SQL is a function with " input + output ", similar to the structure of the function, refer to the above two examples. Among them, parameterType indicates the input parameter type, and resultType indicates the output parameter type. Responding to the above, if we want to prevent SQL injection, it is of course necessary to work hard on input parameters. The part using # in the above code is the part where the input parameters are concatenated in SQL. After passing in the parameters, print out the executed SQL statement, and you will see that the SQL looks like this:

select id, username, password, role from user where username=? and password=?

No matter what parameters are entered, the printed SQL is like this. This is because MyBatis has enabled the precompilation function. Before the SQL is executed, the above SQL will be sent to the database for compilation; when executing, directly use the compiled SQL and replace the placeholder "?". Because SQL injection can only work on the compilation process, this method avoids the problem of SQL injection well.

[Principle of underlying implementation] How does MyBatis achieve SQL precompilation? In fact, at the bottom of the framework, the PreparedStatement class in JDBC is at work. PreparedStatement is a subclass of Statement that we are very familiar with. Its objects contain compiled SQL statements. This "ready" approach can not only improve security, but also improve efficiency when the same SQL is executed multiple times. The reason is that the SQL has been compiled, and there is no need to compile it again when executing it again

Guess you like

Origin blog.csdn.net/2301_77160226/article/details/130414616