忽视SQL注入?你的数据库可能成为下一个攻击目标!

科技   2024-08-05 17:01   上海  

大家好,我是马听,一个DBA界的小学生。

这是曾经在IT界广为流传的一张神图:

当摄像头拍到这个车牌号进行后台处理时,就会触发SQL注入,进行删库操作。

当然,这只是一个段子。

但是站在技术的角度看,很多场景还真可能被SQL注入,这一篇文章就来介绍一下SQL注入是什么?需要怎样预防?


1 什么是SQL注入?

我们先通过一个简单的例子来演示一下SQL注入:

创建测试表并写入数据

use martinCREATE TABLE users_info (    id INT AUTO_INCREMENT PRIMARY KEY,    username VARCHAR(50) NOT NULL,    password VARCHAR(50) NOT NULL,    contact VARCHAR(100) NOT NULL);INSERT INTO users_info (username, password, contact)VALUES ('user1', 'pass1', '123456789'),       ('user2', 'pass2', '987654321'),       ('user3', 'pass3', '121212123'),       ('user4', 'pass4', '989898987');


使用or的SQL注入举例

正常的查询SQL:

select * from users_info where username='user1' and password='pass1';

比如代码里,我们会判断上面的SQL是否执行成功,来判断用户输入的用户名和密码是否匹配。


但是,假如别人不按套路出牌,把密码部分改成

' or 'a'='a

形成的SQL如下:

select * from users_info where username='user1' and password='' or 'a'='a';

不但能通过验证,还能查看到所有用户的数据。

我们知道,数据泄漏可是天大的事。

所以,类似这种恶意构造输入来执行没有权限的数据库操作,我们就称为SQL注入。


使用union的SQL注入

还有一种方式,就是通过union来完成SQL注入。

正常的查询语句:

select * from users_info where username='user1' and password='pass1';

user1替换成

' UNION SELECT * FROM users_info ; -- 

形成的SQL如下:

select * from users_info where username='' UNION SELECT * FROM users_info ; -- ' and password='pass1';

就能查询到表user_info的所有数据了。


2 SQL注入的风险

2.1 数据泄露

攻击者可以绕过访问控制,查询敏感数据。

比如上面的例子,攻击者可以查看到全表的数据,那整张表的所有用户信息都会泄露。


2.2 篡改数据

比如上面的union例子,换成了update语句,就能修改全表的数据,换成delete或者truncate,就能删除全表数据了。

就可能会导致数据丢失,甚至经济损失。


2.3 服务中断

攻击者如果修改一些入口表,可能会导致业务中断。


3 SQL注入的预防

3.1 代码中使用预编译语句的形式

举个栗子:

// 查询数据库匹配姓名和密码query := fmt.Sprintf("SELECT COUNT(*) FROM users_info WHERE username = '%s' AND password = '%s'", name, password)
var count intfmt.Println(query)err = db.QueryRow(query).Scan(&count)
if err != nil {   log.Fatal(err)}

上面的代码,是将用户名和密码直接插入到查询字符串中。

这种方式容易受到SQL注入攻击,因为如果输入的name或password包含SQL语句,都会解释成SQL的一部分,从而也方便了SQL注入。

怎么改写呢?

// 查询数据库匹配姓名和密码query := "SELECT COUNT(*) FROM users_info WHERE username = ? AND password = ?"var count interr = db.QueryRow(query, name, password).Scan(&count)if err != nil {   log.Fatal(err)}

类似这段代码,用问号作为参数占位符,然后使用db.QueryRow(query, name, password)来传递实际的参数。

这种方式可以有效防止SQL注入,因为参数不会被解释为SQL的一部分。


3.2 对输入进行校验和过滤

对用户输入的内容,进行校验,如果发现特殊关键字,则进行过滤。


3.3 权限最小化

为数据库用户配置最小的权限,这样万一出现SQL注入,可以把损失讲到最低,比如上面第二个注入的例子,如果真的权限够,甚至可以植入drop table或者drop database的操作。


3.4 关注自己使用的框架或者包是否存在漏洞

有些框架或者包,本身存在SQL注入的风险,这个时候需要考虑是否需要进行升级或者更换更安全的框架和包。


3.5 审计和监控

如果对于重要的数据库,可以考虑开启审计,监控数据库的一些异常查询。

比如包括1=1,注释符(-- 或 /* */) 等SQL,通过设置告警提醒我们。


好的,关于SQL注入的内容就介绍到这里。


欢迎关注公众号获取更多数据库内容:

近期热文:

一文入门Go语言

MySQL主流高可用方案

建议收藏:一份完整的数据库规范

MySQL主库扛不住了?来试试读写分离吧

MySQL为什么不用Redo Log来进行主从复制?

MySQL数据库联盟
关注后,回复“高可用”,可获取8篇MySQL高可用文章
 最新文章