暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

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

63

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

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

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

当然,这只是一个段子。

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


1 什么是SQL注入?

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

创建测试表并写入数据

    use martin
    CREATE 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 int
                  fmt.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 int
                    err = 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数据库联盟,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                    评论