Detailed explanation of SQL injection with defense - Getting Started

Preface

I'm here again. The last article talked about common commands in Linux. Today I'm going to talk about the process of SQL injection. These are some notes I've compiled since I started studying. I hope you guys can give me some pointers and help improve it.


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 implement the attack, but targets the programmer's negligence when writing. Through SQL statements, it can log in without an account or even tamper with the database.

2: General idea of ​​SQL injection attack

  1. Find the location of SQL injection
  2. Determine server type and backend database type
  3. Conduct SQL injection attacks based on different server and database characteristics

Three: SQL injection attack examples

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=''
--其后果可想而知…
"""

Four: How to defend against SQL injection

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

1. Check 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 If it is to accept an email, then you should check and strictly ensure that the variable must be in the format of the email. The same is true for other types such as date, time, etc. 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. This can largely avoid SQL injection attacks.
  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 It consists of uppercase and lowercase letters, numbers and some safe symbols, and does not contain special characters. At this time we should have a check_username function to perform unified checks. However, there are still many exceptions that cannot be applied to this criterion, such as article publishing systems, comment systems, and other scenarios that must allow users to submit arbitrary strings. This requires the use of filtering and other other solutions.

2. Filter special symbols

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

3. Bind variables and use precompiled statements

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

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

Five: What is sql precompilation?

1. What is a prepared statement?

Usually our sql can be divided into the following three processes after it is received and executed in the db and returned:

   1、词法和语义解析
   2、优化sql语句,制定执行计划
   3、执行并返回结果

We call such ordinary statements 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 query is different, the value of the set clause of update is different, and the value of insert is different. values ​​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 be poor.

The so-called prepared statements are to replace the values ​​in such statements with placeholders, which can be regarded as templating or parameterizing SQL statements. These statements are generally called Prepared Statements or Parameterized Statements   Of course, in terms of optimization, many times the optimal execution plan cannot be determined just by knowing the template of the SQL statement. It is often necessary to estimate the cost through specific values.
  The advantages of precompiled statements are summarized as follows: compile once and run multiple times, eliminating the need for parsing and optimization processes; in addition, precompiled statements can prevent SQL injection.

2. MySQL’s precompilation function

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

Let's take a look at the use of prepared statements in MySQL.

(1) Table creation First we have a test table t with the following structure:

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 next use the syntax of PREPARE stmt_name FROM preparable_stm to precompile a sql statement

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

(3) Execution

We execute prepared statements through the syntax of EXECUTE stmt_name [USING @var_name [, @var_name] ...]

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 prepared statements in MySQL is session level, but we can control the global maximum stored precompiled statements through the max_prepared_stmt_count variable.

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 reaches the threshold, you can see that MySQL will report the error shown above.

(4) Release
  If we want to release a prepared statement, we can use the syntax of {DEALLOCATE | DROP} PREPARE stmt_name:

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

Six: 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 parameters passed in, thus avoiding the occurrence of SQL injection. Detailed analysis below

(1) Why is Statement injected by SQL?

Because Statement will be 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 was originally counted only when the username and password match, but after the change, it became a logical relationship of OR. Regardless of whether the username 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 in the database before receiving user input. In this way, no matter what user name and password the user enters, the judgment will always be consistent and logical, preventing SQL injection.

To summarize briefly, the reason why parameterization can prevent injection is that the statement is a statement, the parameters are parameters, and the value of the parameter is not part of the statement. The database only runs according to the semantics of the statement. As for running with an ordinary backpack or a monster, It won't affect the traveling route, it's just the difference between running faster and slower.

Seven: How does mybatis prevent SQL injection?

1. First, take a 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 a double quotation mark 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, The parsed sql is where username="id". 
  2. Directly display and generate the incoming data in s q l. For example: w h e r e u s e r n a m e = The incoming data is directly displayed and generated in sql. For example: where username= Generalnumber installationdirectconnectionexhibitionshow borngrowncurrentsq lmiddle. aswhe reuse rname ={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. # method can Prevent sql injection to a great extent. method cannot prevent sql injection. 4. This method cannot prevent Sql injection. 4. wayexpressionwithoutlawdefense StopSqlNote enter. 4,   Method general use 于传入数库对例,例文传传入表名.
  5, General ability use,ifnotgetnot Use using parameters like "{xxx}", and you need to do the filtering work manually. , to prevent sql injection attacks.
  6. In MyBatis, parameters in the format of " x x x" will directly participate in SQL compilation, so injection attacks cannot be avoided. But when it comes to dynamic table names and column names, you can only use parameters in the format of "{xxx}", which will directly participate in SQL compilation, so injection attacks cannot be avoided. But when it comes to dynamic table names and column names, you can only use " xxxcaseexpressiontermsee NumberMeetingDirectContactMeaning giveSQLed HoweverNot NohEscapeExemptNoteEnter attack.Howeverandtoactionpositiontable namesumcolumnnametime ,justnohuseuse {xxx}” This is the regular number case. Therefore, this number demand is injected to prevent the progress of manual work in the current generation.
[Summary] When copying MyBatis' projection language, use "#{xxx}" as the official formality.You should not use "${xxx}" as the official number, the required construction site is good, and the SQL injection attack to prevent it. .
How can mybatis prevent SQL injection

As a semi-automated persistence layer framework, the MyBatis framework requires us to manually write its SQL statements ourselves. Of course, we need to prevent SQL injection at this time. In fact, MyBatis's SQL is a function with "input + output", similar to the structure of a function. Please refer to the two examples above. Among them, parameterType represents the input parameter type, and resultType represents the output parameter type. In response to the above, if we want to prevent SQL injection, we naturally need to work hard on input parameters. The # used in the above code is the part where the input parameters are spliced ​​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 SQL printed out will be like this. This is because MyBatis has enabled the pre-compilation function. Before the SQL is executed, the above SQL will be sent to the database for compilation; during execution, the compiled SQL will be used directly and the placeholder "?" will be replaced. Because SQL injection can only work on the compilation process, this method can effectively avoid the problem of SQL injection.

Summarize

[Underlying Implementation Principle] How does MyBatis achieve SQL pre-compilation? 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, and its object contains compiled SQL statements. This "prepared" approach not only improves security, but also improves efficiency when executing the same SQL multiple times. The reason is that the SQL has been compiled and does not need to be compiled again when executed again.

Guess you like

Origin blog.csdn.net/YJ000312/article/details/123865630