SpringBoot防止SQL注入XSS攻击CSRFCROS
一、SQL注入问题
(1)什么是SQL注入
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
简单来说,就是将大部分SQL语句当参数传入系统中,从而获取系统中的数据。下面简单举例说明
系统中有这样一条信息SQL语句执行,分页查询所有用户,每页查询20条,并且根据指定字段进行排序,也就是说排序字段也是参数传递过来的selectfromuserinfoorderby{fieldName}desclimit{page},{limit};正常执行的SQLselectfromuserinfoorderbyiddesclimit0,20;假如说,懂行的人恶意将排序参数修改为以下格式selectfromuserinfoorderbyid;select1desclimit0,20;
SQL注入问题分析:攻击者注入了类似这样的参数:1;缩表语句其中;前面的语句先执行了由于后面的语句会被注释掉,接下来只会执行锁表语句,把表锁住正常业务请求从数据库获得连接之后,尝试获取锁表,但是一直获取不到,直到超时数据库连接池不够用,没有空闲连接新的业务请求获取不到数据库连接,报错数据库连接过多异常
这样很简单的一句话SQL,就可以把系统搞炸掉,这种方式可以实现删库跑路1;deletefromuser;
以上语句会把整个test数据库所有内容都删掉(2)防止SQL注入使用预编译机制
尽量用预编译机制,少用字符串拼接的方式传参,它是sql注入问题的根源。要对特殊字符进行转义
有些特殊字符,比如:作为like语句中的参数时,要对其进行转义处理。要捕获异常
需要对所有的异常情况进行捕获,切记接口直接返回异常信息,因为有些异常信息中包含了sql信息,包括:库名,表名,字段名等。攻击者拿着这些信息,就能通过sql注入随心所欲地攻击你的数据库了。目前比较主流的做法是,有个专门的网关服务,它统一暴露对外接口。用户请求接口时先经过它,再由它将请求转发给业务服务。这样做的好处是:能统一封装返回数据的返回体,并且如果出现异常,能返回统一的异常信息,隐藏敏感信息。此外还能做限流和权限控制。使用代码检测工具
使用sqlMap等待代码检测工具,它能检测sql注入漏洞。要有监控
需要对数据库sql的执行情况进行监控,有异常情况,及时邮件或短信提醒。数据库账号需控制权限
对生产环境的数据库建立单独的账号,只分配DML相关权限,且不能访问系统表。切勿在程序中直接使用管理员账号。代码review
建立代码review机制,能找出部分隐藏的问题,提升代码质量。使用其他手段处理
对于不能使用预编译传参时,要么开启druid的filter防火墙,要么自己写代码逻辑过滤掉所有可能的注入关键字。二、XSS攻击问题(1)什么是XSS攻击
XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、VBScript、ActiveX、Flash或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。
通常情况下,被用来盗用Cookie、破坏页面结构、重定向到其他网站等(2)防止XSS攻击
对用户输入的表单信息进行检测过滤三、CSRFCROS恶意访问(1)什么是CSRFCROS恶意访问
CSRFCrossSiteRequestForgery跨站请求伪造:
攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在未授权的情况下执行在权限保护之下的操作,CORSCrossOriginResourseSharing跨站资源共享,恶意访问内网敏感资源。(2)解决办法
有效的解决办法是通过多种条件屏蔽掉非法的请求,例如HTTP头、参数等:不信任未经身份验证的跨域请求,应该首先验证SessionID或者Cookie对于请求方来说验证接收的数据有效性,服务方仅暴露最少最必须的功能通过多种条件屏蔽掉非法的请求,例如HTTP头、参数等服务端代码跨域设置,设置了nginx转发,也可设置在nginx中
防止大规模的恶意请求,niginx反向代理可以配置请求频率,对ip做限制。nginx可以很方便地做访问控制,特别是一些偶发性的大量恶意请求,需要屏蔽处理。
屏蔽ip地址locationsomeapi{allowip;特定接口只开放给某个ip调用denyall;}locationsomepage{denyip;屏蔽某个ip访问(iptables可以拒绝某个ip连接)allowall;}
屏蔽useragentif(httpuseragentMozilla5。0){return403;}有些请求头很明显不是用户浏览器分析nginxahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a,找出恶意ip或useragentcatvarlognginxaccess。logawkF{A〔(NF1)〕}END{for(kinA)printA〔k〕,k}sortntail12258。144。7。66337106。91。201。752270122。200。77。170显然这个ip不正常,而且这不是nginx所知道的真实ip,而是httpxforwardedfor变量
屏蔽代理ip
有两种情形会需要屏蔽代理ip:一是代理ip访问,二是负载均衡(realip请求负载均衡服务器,再代理给后端server)vietcnginxbadproxy。rulesmaphttpxforwardedforbadproxy{default0;122。200。77。1701;建立映射}vietcnginxnginx。confhttp{includeetcnginxbadproxy。rules这个要在server配置之前server{locationsomepage{if(badproxy){return403;}}}}四、解决方案
创建XssAndSqlHttpServletRequestWrapper包装器,这是实现XSS过滤的关键,在其内重写了getParameter,getParameterValues,getHeader等方法,对http请求内的参数进行了过滤importjava。io。BufferedReader;importjava。io。ByteArrayInputStream;importjava。io。IOException;importjava。io。InputStreamReader;importjava。util。Enumeration;importjava。util。HashMap;importjava。util。Map;importjava。util。Set;importjava。util。Vector;importjava。util。regex。Pattern;importjavax。servlet。ReadListener;importjavax。servlet。ServletInputStream;importjavax。servlet。http。HttpServletRequest;importjavax。servlet。http。HttpServletRequestWrapper;importorg。springframework。util。StreamUtils;importcom。sgcc。uap。utils。stream。StreamUtil;publicclassXssAndSqlHttpServletRequestWrapperextendsHttpServletRequestWrapper{HttpServletRequestorgRequestnull;privateMapString,String〔〕parameterMap;privatefinalbyte〔〕body;用于保存读取body中数据publicXssAndSqlHttpServletRequestWrapper(HttpServletRequestrequest)throwsIOException{super(request);orgRequestrequest;parameterMaprequest。getParameterMap();bodyStreamUtils。copyToByteArray(request。getInputStream());}重写几个HttpServletRequestWrapper中的方法获取所有参数名return返回所有参数名OverridepublicEnumerationgetParameterNames(){VectorvectornewVector(parameterMap。keySet());returnvector。elements();}覆盖getParameter方法,将参数名和参数值都做xsssql过滤。
如果需要获得原始的值,则通过super。getParameterValues(name)来获取
getParameterNames,getParameterValues和getParameterMap也可能需要覆盖OverridepublicStringgetParameter(Stringname){String〔〕resultsparameterMap。get(name);if(resultsnullresults。length0)returnnull;else{Stringvalueresults〔0〕;if(value!null){valuexssEncode(value);}returnvalue;}}获取指定参数名的所有值的数组,如:checkbox的所有数据接收数组变量,如checkobx类型OverridepublicString〔〕getParameterValues(Stringname){String〔〕resultsparameterMap。get(name);if(resultsnullresults。length0)returnnull;else{intlengthresults。length;for(inti0;ilength;i){results〔i〕xssEncode(results〔i〕);}returnresults;}}覆盖getHeader方法,将参数名和参数值都做xsssql过滤。
如果需要获得原始的值,则通过super。getHeaders(name)来获取
getHeaderNames也可能需要覆盖OverridepublicStringgetHeader(Stringname){Stringvaluesuper。getHeader(xssEncode(name));if(value!null){valuexssEncode(value);}returnvalue;}将容易引起xsssql漏洞的半角字符直接替换成全角字符paramsreturnprivatestaticStringxssEncode(Strings){if(snulls。isEmpty()){returns;}else{sstripXSSAndSql(s);}StringBuildersbnewStringBuilder(s。length()16);for(inti0;is。length();i){charcs。charAt(i);switch(c){case:sb。append();转义大于号break;case:sb。append();转义小于号break;case:sb。append();转义单引号break;case:sb。append();转义双引号break;case:sb。append();转义break;case:sb。append();转义break;default:sb。append(c);break;}}returnsb。toString();}获取最原始的requestreturnpublicHttpServletRequestgetOrgRequest(){returnorgRequest;}获取最原始的request的静态方法returnpublicstaticHttpServletRequestgetOrgRequest(HttpServletRequestreq){if(reqinstanceofXssAndSqlHttpServletRequestWrapper){return((XssAndSqlHttpServletRequestWrapper)req)。getOrgRequest();}returnreq;}防止xss跨脚本攻击(替换,根据实际情况调整)publicstaticStringstripXSSAndSql(Stringvalue){if(value!null){NOTE:ItshighlyrecommendedtousetheESAPIlibraryanduncommentthefollowinglinetoavoidencodedattacks。valueESAPI。encoder()。canonicalize(value);Avoidnullcharactersvaluevalue。replaceAll(,);AvoidanythingbetweenscripttagsPatternscriptPatternPattern。compile(〔r〕script〔r〕(。?)〔r,Pattern。CASEINSENSITIVE);valuescriptPattern。matcher(value)。replaceAll();Avoidanythinginasrca2020imgdataimg。jpgdatasrchttp:www。yihaomen。comarticlejava。。。typeofexpressionscriptPatternPattern。compile(src〔r〕〔r〕〔〕(。?)〔〕,Pattern。CASEINSENSITIVEPattern。MULTILINEPattern。DOTALL);valuescriptPattern。matcher(value)。replaceAll();RemoveanylonesomescripttagscriptPatternPattern。compile(〔r,Pattern。CASEINSENSITIVE);valuescriptPattern。matcher(value)。replaceAll();RemoveanylonesometagscriptPatternPattern。compile(〔r,Pattern。CASEINSENSITIVE);flagscriptPattern。matcher(value)。find();if(flag){returnflag;}Removeanylonesome