不知道各位面试时,有没有相关的面试官有没有问到这样的问题,什么是sql注入,sql注入的危害是什么,mybatis的#与$的区别是什么等等,我想很多人都知道使用mybatis的#去规避sql注入,但是很多人不知道其原理,且对sql注入的危害没有明显的感受,本文将给小伙伴们分享一期SQL注入的知识点,让大家以后能够规避这类问题。

01 什么是SQL注入

SQL注入(SQL Injection)是一种常见的Web安全漏洞,形成的主要原因是web应用程序在接收相关数据参数时未做好过滤,将其直接带入到数据库中查询,导致攻击者可以拼接执行构造的SQL语句。

即:注入产生的原因是后台服务器在接收相关参数时未做好过滤直接带入到数据库中查询,导致可以拼接执行构造的SQL语句。

SQL注入的攻击流程
1、判断注入类型(数字型or字符型)

2、猜解SQL查询语句中的字段数:1’ or 1=1 order by 10

3、确定字段的显示顺序:1’ union select 1,2

4、获取当前数据库:1’ union select 1,database()

5、获取数据库中的表:1’ union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()

6、获取表中的字段名:1’ union select 1,group_concat(cloumn_name) from information_schema.columns where table_name=‘users’

7、下载数据:1’ union select 1,group_concat(id,username,password) from users

02 SQL注入实例

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=‘’

–其后果可想而知…

03 SQL注入危害

SQL注入漏洞对于数据安全的影响:

数据库信息泄漏:数据库中存放的用户的隐私信息的泄露。

网页篡改:通过操作数据库对特定网页进行篡改。

网站被挂马,传播恶意软件:修改数据库一些字段的值,嵌入网马链接,进行挂马攻击。

数据库被恶意操作:数据库服务器被攻击,数据库的系统管理员帐户被窜改。

服务器被远程控制,被安装后门:经由数据库服务器提供的操作系统支持,让黑客得以修改或控制操作系统。

破坏硬盘数据,瘫痪全系统

比如前几年的一个sql注入的大事件,俄罗斯技术社区网站Habrahabr上,一名昵称为NoraQ的用户(黑客)2018年1月29日发文称,1400万名俄罗斯大学毕业生信息泄露。俄罗斯总人口数量约为1.46亿,即十分之一俄罗斯人的信息泄露。泄漏信息包括姓名、出生日期、个人账户的保险号码、纳税人识别号码、电子邮件地址等,文件大小约为5GB。

NoraQ在俄罗斯联邦教育科学联督局服务网站上发现了一个SQL注入漏洞,通过这个漏洞他下载了上述1400万名毕业生信息。

04 常见的注入手法

Union注入
真没想到,SQL注入漏洞的这么大,竟然导致1400万名俄罗斯大学毕业生信息泄露-LMLPHP

报错注入
真没想到,SQL注入漏洞的这么大,竟然导致1400万名俄罗斯大学毕业生信息泄露-LMLPHP

堆叠注入
真没想到,SQL注入漏洞的这么大,竟然导致1400万名俄罗斯大学毕业生信息泄露-LMLPHP
时间注入
真没想到,SQL注入漏洞的这么大,竟然导致1400万名俄罗斯大学毕业生信息泄露-LMLPHP
HTTP头注入
真没想到,SQL注入漏洞的这么大,竟然导致1400万名俄罗斯大学毕业生信息泄露-LMLPHP
产生注入的条件:

  • 能够对请求头消息进行修改

  • 修改的请求头信息能够带入数据库进行查询

  • 数据库没有对输入的请求信息做过滤

宽字节注入
问题:单引号被转义(前面加了个 \ )
例:%df%5c表示的是“連"字,而%5c刚好是\的编码
如果查表名引号被转义,用burpsuite的Decoder---->Encode as----->ASCII hex—转16进制。

二阶注入
二次注入漏洞是一种在Web应用程序中广泛存在的安全漏洞形式。相对于一次注入漏洞而言,二次注入漏洞更难以被发现,但是它却具有与—次注入攻击漏洞相同的攻击威力。

1、黑客通过构造数据的形式,在浏览器或者其他软件中提交HTTP数据报文请求到服务端进行处理,提交的数据报文请求中可能包含了黑客构造的SQL语句或者命令。

2、服务端应用程序会将黑客提交的数据信息进行存储,通常是保存在数据库中,保存的数据信息的主要作用是为应用程序执行其他功能提供原始输入数据并对客户端请求做出响应。

3、黑客向服务端发送第二个与第一次不相同的请求数据信息。

4、服务端接收到黑客提交的第二个请求信息后,为了处理该请求,服务端会查询数据库中已经存储的数据信息并处理,从而导致黑客在第一次请求中构造的SQL语句或者命令在服务端环境中执行。

5、服务端返回执行的处理结果数据信息,黑客可以通过返回的结果数据信息判断二次注入漏洞利用是否成功

总结,二次注入就是由于将数据存储进数据库中时未做好过滤,先提交构造好的特殊字符请求存储进数据库,然后提交第二次请求时与第一次提交进数据库中的字符发生了作用,形成了一条新的sql语句导致被执行。以sqli-labs第24关为例。

05 如何发现SQL注入漏洞

1. 注入可能存在的地方

竟然是sql注入,那么这个地方肯定是与数据库有数据交互的,所以我们可以优先观察那种页面存在传值或者查询的地方。比如url中的GET型传参,如?id=1
如我们看见这种就可以考虑
真没想到,SQL注入漏洞的这么大,竟然导致1400万名俄罗斯大学毕业生信息泄露-LMLPHP
或者是搜索框,前端将用户输入的数据代入到数据库中进行查询,这种以POST方法进行发送数据。如下这种地方

真没想到,SQL注入漏洞的这么大,竟然导致1400万名俄罗斯大学毕业生信息泄露-LMLPHP

或者是HTTP请求头部字段如Cookie值,下面会讲到。

2. 漏洞探测

此时需要我们用burp截取查询的数据包,找到传参的变量然后在其后面加上单引号、双引号等如下payload进行测试。

06 如何避免SQL注入

解决SQL注入问题的关键是对所有可能来自用户输入的数据进行严格的检查、对数据库配置使用最小权限原则。通常修复使用的方案有:

代码层面:

1、对输入进行严格的转义和过滤,校验字符串。

过滤输入内容就是在数据提交到数据库之前,就把用户输入中不合法的字符剔除掉,可以使用编程语言提供的处理函数或自己的处理函数来进行过滤,还可以使用正则表达式匹配安全的字符串。

如果值属于特定的类型或有具体的格式,那么在拼接SQL语句之前就要进行校验,验证其有效性,比如对于某个传入的值,如果可以确定是整形,则要判断他是否为整型,在浏览器端(客户端)和服务器端都要进行验证。

2、使用参数化(Parameterized),使用预编译语句。

预编译语句对现在的程序员来说基本都会去设计使用的方法,保障数据库的安全。一般来说,防御SQL注入的最佳方式就是使用预编译语句,绑定变量。

String query="select password from users where username=‘?’ ";

网络层面:

1、通过WAF设备启用防SQL Inject注入策略(或类似防护系统)

2、云端防护(如阿里云盾)

安全测试,安全审计:
除了开发规范,还需要合适的工具来确保代码的安全。我们应该在开发过程中应对代码进行审查,在测试环节使用工具进行扫描,上线后定期扫描安全漏洞。通过多个环节的检查,一般是可以避免 SQL 注入的。

有些人认为存储过程可以避免 SQL 注入,存储过程在传统行业里用得比较多,对于权限的控制是有一定用处的,但如果存储过程用到了动态查询,拼接 SQL,一样会存在安全隐患。

下面是在开发过程中可以避免 SQL 注入的一些方法。

1.避免使用动态SQL

避免将用户的输入数据直接放在SQL语句中,最好使用准备好的语句和参数化查询,这样更安全。

2.不要将敏感数据保留在纯文本中

加密存储在数据库中的私有/机密数据,这样可以提供了另一级保护,以防攻击者成功的排出敏感数据

3.限制数据库的权限和特权

将数据库用户的功能设置为最低要求;这将限制攻击者在设法获取访问权限时可以执行的操作

4.避免直接向用户显示数据库错误

攻击者可以使用这些错误消息来获取有关的数据库信息。

07 Mybatis如何防止SQL注入的

观察两段代码区别:
真没想到,SQL注入漏洞的这么大,竟然导致1400万名俄罗斯大学毕业生信息泄露-LMLPHP
mybatis中#和$的区别:
1、#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。

如:where username=#{username},如果传入的值是111,那么解析成sql时的值为where username=“111”, 如果传入的值是id,则解析成的sql为where username=“id”.

2、$将传入的数据直接显示生成在sql中。

如:where username=${username},如果传入的值是111,那么解析成sql时的值为where username=111;

如果传入的值是;drop table user;,则解析成的sql为:select id, username, password, role from user where username=;drop table user;

3、#能很大程度防止sql注入,$ 方式无法防止sql注入

4、$方式一般用于传入数据库对象,例如传入表名.

5、一般能用#的就别用 ,若不得不使用“ ,若不得不使用“ ,若不得不使用{xxx}”这样的参数,要手工地做好过滤工作,来防止sql注入攻击。

6、在MyBatis中,“ x x x ”这样格式的参数会直接参与 S Q L 编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“ {xxx}”这样格式的参数会直接参与SQL编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“ xxx这样格式的参数会直接参与SQL编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用{xxx}”这样的参数格式。所以,这样的参数需要我们在代码中手工进行处理来防止注入。

【结论】在编写MyBatis的映射语句时,尽量采用“#{xxx}”这样的格式。若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止SQL注入攻击。

mybatis是如何做到防止SQL注入的
MyBatis框架作为一款半自动化的持久层框架,其SQL语句都要我们自己手动编写,这个时候当然需要防止SQL注入。其实,MyBatis的SQL是一个具有“输入+输出”的功能,类似于函数的结构,参考上面的两个例子。其中,parameterType表示了输入的参数类型,resultType表示了输出的参数类型。回应上文,如果我们想防止SQL注入,理所当然地要在输入参数上下功夫。上面代码中使用#的即输入参数在SQL中拼接的部分,传入参数后,打印出执行的SQL语句,会看到SQL是这样的:

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

不管输入什么参数,打印出的SQL都是这样的。这是因为MyBatis启用了预编译功能,在SQL执行前,会先将上面的SQL发送给数据库进行编译;执行时,直接使用编译好的SQL,替换占位符“?”就可以了。因为SQL注入只能对编译过程起作用,所以这样的方式就很好地避免了SQL注入的问题。

【底层实现原理】MyBatis是如何做到SQL预编译的呢?其实在框架底层,是JDBC中的PreparedStatement类在起作用,PreparedStatement是我们很熟悉的Statement的子类,它的对象包含了编译好的SQL语句。这种“准备好”的方式不仅能提高安全性,而且在多次执行同一个SQL时,能够提高效率。原因是SQL已编译好,再次执行时无需再编译。

最后,如果我的文章对你有所帮助或者有所启发,欢迎关注公众号(微信搜索公众号:首席架构师专栏),里面有许多技术干货,也有我对技术的思考和感悟,还有作为架构师的验验分享;关注后回复 【面试题】,有我准备的面试题、架构师大型项目实战视频等福利 , 小编会带着你一起学习、成长,让我们一起加油!!!

03-28 10:30