自己做网站花费,中铁十六门户登录,企业网站建设可行性分析 技能训练,动易网站 修改栏目名字前言最近我在整理安全漏洞相关问题#xff0c;准备在公司做一次分享。恰好#xff0c;这段时间团队发现了一个sql注入漏洞#xff1a;在一个公共的分页功能中#xff0c;排序字段作为入参#xff0c;前端页面可以自定义。在分页sql的mybatis mapper.xml中#xff0c;orde… 前言最近我在整理安全漏洞相关问题准备在公司做一次分享。恰好这段时间团队发现了一个sql注入漏洞在一个公共的分页功能中排序字段作为入参前端页面可以自定义。在分页sql的mybatis mapper.xml中order by字段后面使用$符号动态接收计算后的排序参数这样可以实现动态排序的功能。但是如果入参传入id; select 1 --
最终执行的sql会变成select * from user order by id; select 1 -- limit 1,20
--会把后面的limit语句注释掉导致分页条件失效返回了所有数据。攻击者可以通过这个漏洞一次性获取所有数据。动态排序这个功能原本的想法是好的但是却有sql注入的风险。值得庆幸的是这次我们及时发现了问题并且及时解决了没有造成什么损失。但是几年前在老东家的时候就没那么幸运了。一次sql注入直接把我们支付服务搞挂了。1. 还原事故现场有一天运营小姐姐跑过来跟我说有很多用户支付不了。这个支付服务是一个老系统转手了3个人了一直很稳定没有出过啥问题。我二话不说开始定位问题了先看服务器日志发现了很多报数据库连接过多的异常。因为支付功能太重要了当时为了保证支付功能快速恢复先找运维把支付服务2个节点重启了。5分钟后暂时恢复了正常。我再继续定位原因据我当时的经验判断一般出现数据库连接过多可能是因为连接忘了关闭导致。但是仔细排查代码没有发现问题我们当时用的数据库连接池它会自动回收空闲连接的排除了这种可能。过了会儿又有一个节点出现了数据库连接过多的问题。但此时还没查到原因逼于无奈只能让运维再重启服务不过这次把数据库最大连接数调大了默认是100我们当时设置的500后面调成了1000。其实现在大部分公司会将这个参数设置成1000使用命令set GLOBAL max_connections500;
能及时生效不需要重启mysql服务。这次给我争取了更多的时间找dba帮忙一起排查原因。使用show processlist;命令查看当前线程执行情况还可以查看当前的连接状态帮助识别出有问题的查询语句。需要特别说明的是上图只是我给的一个例子线上真实的结果不是这样的id 线程idUser 执行sql的账号Host 执行sql的数据库的ip和端号db 数据库名称Command 执行命令包括Daemon、Query、Sleep等。Time 执行sql所消耗的时间State 执行状态info 执行信息里面可能包含sql信息。果然发现了一条不寻常的查询sql执行了差不多1个小时还没有执行完。dba把那条sql复制出来发给我了。然后kill -9 杀掉了那条执行耗时非常长的sql线程。后面数据库连接过多的问题就没再出现了。我拿到那条sql仔细分析了一下发现一条订单查询语句被攻击者注入了很长的一段sql肯定是高手写的有些语法我都没见过。但可以确认无误被人sql注入了。通过那条sql中的信息我很快找到了相关代码查询数据时入参竟然用的Statment而非PrepareStatement预编译机制。知道原因就好处理了将查询数据的地方改成preparestatement预编译机制后问题得以最终解决。2.为什么会导致数据库连接过多我相信很多同学看到这里都会有一个疑问sql注入为何会导致数据库连接过多我下面用一张图给大家解释一下攻击者sql注入了类似这样的参数-1;锁表语句--。其中;前面的查询语句先执行了。由于--后面的语句会被注释接下来只会执行锁表语句把表锁住。正常业务请求从数据库连接池成功获取连接后需要操作表的时候尝试获取表锁但一直获取不到直到超时。注意这里可能会累计大量的数据库连接被占用没有及时归还。数据库连接池不够用没有空闲连接。新的业务请求从数据库连接池获取不到连接报数据库连接过多异常。sql注入导致数据库连接过多问题最根本的原因是长时间锁表。3.预编译为什么能防sql注入preparestatement预编译机制会在sql语句执行前对其进行语法分析、编译和优化其中参数位置使用占位符?代替了。当真正运行时传过来的参数会被看作是一个纯文本不会重新编译不会被当做sql指令。这样即使入参传入sql注入指令如id; select 1 --
最终执行的sql会变成select * from user order by id; select 1 -- limit 1,20
这样就不会出现sql注入问题了。4.预编译就一定安全不知道你在查询数据时有没有用过like语句比如查询名字中带有“苏”字的用户就可能会用类似这样的语句查询select * from user where name like %苏%;
正常情况下是没有问题的。但有些场景下要求传入的条件是必填的比如name是必填的如果注入了%最后执行的sql会变成这样的select * from user where name like %%%;
这种情况预编译机制是正常通过的但sql的执行结果不会返回包含%的用户而是返回了所有用户。name字段必填变得没啥用了攻击者同样可以获取用户表所有数据。为什么会出现这个问题呢%在mysql中是关键字如果使用like %%%该like条件会失效。如何解决呢需要对%进行转义\%。转义后的sql变成select * from user where name like %\%%;
只会返回包含%的用户。5.有些特殊的场景怎么办在java中如果使用mybatis作为持久化框架在mapper.xml文件中如果入参使用#传值会使用预编译机制。一般我们是这样用的sql idqueryselect * from user wherename #{name}/where
/sql
绝大多数情况下鼓励大家使用#这种方式传参更安全效率更高。但是有时有些特殊情况比如sql idorderByorder by ${sortString}
/sql
sortString字段的内容是一个方法中动态计算出来的这种情况是没法用#代替$的这样程序会报错。使用$的情况就有sql注入的风险。那么这种情况该怎办呢自己写个util工具过滤掉所有的注入关键字动态计算时调用该工具。如果数据源用的阿里的druid的话可以开启filter中的wall防火墙它包含了防止sql注入的功能。但是有个问题就是它默认不允许多语句同时操作对批量更新操作也会拦截这就需要我们自定义filter了。6.表信息是如何泄露的有些细心的同学可能会提出一个问题在上面锁表的例子中攻击者是如何拿到表信息的方法1盲猜就是攻击者根据常识猜测可能存在的表名称。假设我们有这样的查询条件select * from t_order where id ${id};
传入参数-1;select * from user最终执行sql变成select * from t_order where id -1; select * from user;
如果该sql有数据返回说明user表存在被猜中了。建议表名不要起得过于简单可以带上适当的前缀比如t_user。这样可以增加盲猜的难度。方法2通过系统表其实mysql有些系统表可以查到我们自定义的数据库和表的信息。假设我们还是以这条sql为例select code,name from t_order where id ${id};
第一步获取数据库和账号名。传参为-1 union select database(),user()#最终执行sql变成select code,name from t_order where id -1 union select database(),user()#
会返回当前 数据库名称sue 和 账号名称rootlocalhost。第二步获取表名。传参改成-1 union select table_name,table_schema from information_schema.tables where table_schemasue#最终执行sql变成select code,name from t_order where id -1 union select table_name,table_schema from information_schema.tables where table_schemasue#
会返回数据库sue下面所有表名。建议在生成环境程序访问的数据库账号要跟管理员账号分开一定要控制权限不能访问系统表。7.sql注入到底有哪些危害1. 核心数据泄露大部分攻击者的目的是为了赚钱说白了就是获取到有价值的信息拿出去卖钱比如用户账号、密码、手机号、身份证信息、银行卡号、地址等敏感信息。他们可以注入类似这样的语句-1; select * from user; --
就能轻松把用户表中所有信息都获取到。所以建议大家对这些敏感信息加密存储可以使用AES对称加密。2. 删库跑路也不乏有些攻击者不按常理出牌sql注入后直接把系统的表或者数据库都删了。他们可以注入类似这样的语句-1; delete from user; --
以上语句会删掉user表中所有数据。-1; drop database test; --
以上语句会把整个test数据库所有内容都删掉。正常情况下我们需要控制线上账号的权限只允许DMLdata manipulation language数据操纵语言语句包括select、update、insert、delete等。不允许DDLdata definition language数据库定义语言语句包含create、alter、drop等。也不允许DCLData Control Language数据库控制语言语句包含grant,deny,revoke等。DDL和DCL语句只有dba的管理员账号才能操作。顺便提一句如果被删表或删库了其实还有补救措施就是从备份文件中恢复可能只会丢失少量实时的数据所以一定有备份机制。3. 把系统搞挂有些攻击者甚至可以直接把我们的服务搞挂了在老东家的时候就是这种情况。他们可以注入类似这样的语句-1;锁表语句;--
把表长时间锁住后可能会导致数据库连接耗尽。这时我们需要对数据库线程做监控如果某条sql执行时间太长要邮件预警。此外合理设置数据库连接的超时时间也能稍微缓解一下这类问题。从上面三个方面能看出sql注入问题的危害真的挺大的我们一定要避免该类问题的发生不要存着侥幸的心理。如果遇到一些不按常理出票的攻击者一旦被攻击了你可能会损失惨重。8. 如何防止sql注入1. 使用预编译机制尽量用预编译机制少用字符串拼接的方式传参它是sql注入问题的根源。2. 要对特殊字符转义有些特殊字符比如%作为like语句中的参数时要对其进行转义处理。3. 要捕获异常需要对所有的异常情况进行捕获切记接口直接返回异常信息因为有些异常信息中包含了sql信息包括库名表名字段名等。攻击者拿着这些信息就能通过sql注入随心所欲的攻击你的数据库了。目前比较主流的做法是有个专门的网关服务它统一暴露对外接口。用户请求接口时先经过它再由它将请求转发给业务服务。这样做的好处是能统一封装返回数据的返回体并且如果出现异常能返回统一的异常信息隐藏敏感信息。此外还能做限流和权限控制。4. 使用代码检测工具使用sqlMap等代码检测工具它能检测sql注入漏洞。5. 要有监控需要对数据库sql的执行情况进行监控有异常情况及时邮件或短信提醒。6. 数据库账号需控制权限对生产环境的数据库建立单独的账号只分配DML相关权限且不能访问系统表。切勿在程序中直接使用管理员账号。7. 代码review建立代码review机制能找出部分隐藏的问题提升代码质量。8. 使用其他手段处理对于不能使用预编译传参时要么开启druid的filter防火墙要么自己写代码逻辑过滤掉所有可能的注入关键字。