springboot-防止sql注入,xss攻击,cros恶意访问
完整代码下载链接:
https://github.com/2010yhh/springBoot-demos.git
环境
idea2018,jdk1.8,
springboot版本:springboot1.5.9.RELEASE
1.sql注入
sql注入:
把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令
解决方法:
1)无论是直接使用数据库还是使用如mybatis组件,使用sql的预编译,不要用拼接字符串。
2)后台过滤检测:使用正则表达式过滤传入的参数**;**.字符串过滤
3)前端检测sql常见关键字,如or and drop之类的
测试:
1.sql直接拼接,访问:http://localhost:8180/webapp2/testSql1?userName=1&passWord=1 or 1=1
实际执行sql:sql1:select user_name,pass_word from cas_user where user_name= '1' and pass_word=1 INVALID 1=1
- 1
- 2
结果:输入错误信息也能查询
2.sql预编译(其他mybais组件类似),访问:http://localhost:8180/webapp2/testSql2?userName=1&passWord=1 or 1=1
实际执行sql:sql2:select user_name,pass_word from cas_user where user_name= '1' and pass_word= '1 INVALID 1=1'
- 1
结果:
2种不同的sql写法:
3.代码中增加了过滤器检测表单输入后,sql关键字段会被过滤掉;访问:http://localhost:8180/webapp2/testSql2?userName=1&passWord=1 or 1=1
结果:非法输入被过滤器过滤掉了(或者替换了)
2.xss攻击
xss攻击:
其原理是攻击者向有XSS漏洞的网站中输入(传入)恶意的HTML代码,当其它用户浏览该网站时,这段HTML代码会自动执行,从而达到攻击的目的。如,盗取用户Cookie、破坏页面结构、重定向到其它网站等
解决方法:对用户输入的表单信息进行检测过滤
测试:访问:http://localhost:8180/webapp2/testXss?userName=1&passWord=
结果:非法输入被过滤器过滤掉了
3.csrf/cros
关于csrf/cros攻击的详细介绍:
https://www.cnblogs.com/lailailai/p/4528092.html
关于跨域的详细介绍:
https://www.cnblogs.com/keyi/p/6726089.html
CSRF - Cross-Site Request Forgery - 跨站请求伪造:
攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在未授权的情况下执行在权限保护之下的操作
CORS - Cross Origin Resourse-Sharing - 跨站资源共享:
恶意访问内网敏感资源
解决方法:
有效的解决办法是通过多种条件屏蔽掉非法的请求,例如HTTP头、参数等
1、不信任未经身份验证的跨域请求,应该首先验证Session ID或者Cookie。
2、对于请求方来说验证接收的数据有效性,服务方仅暴露最少最必须的功能。
3、通过多种条件屏蔽掉非法的请求,例如HTTP头、参数等。
服务端代码跨域设置,设置了nginx转发,也可设置在nginx中:
4.服务端代码处理,以springboot为例:
可利用过滤器进行设置,如下所示:
@WebFilter public class CrosXssFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(CrosXssFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); //跨域设置 if(response instanceof HttpServletResponse){ HttpServletResponse httpServletResponse=(HttpServletResponse)response; //通过在响应 header 中设置 ‘*’ 来允许来自所有域的跨域请求访问。 httpServletResponse.setHeader("Access-Control-Allow-Origin", "*"); //通过对 Credentials 参数的设置,就可以保持跨域 Ajax 时的 Cookie //设置了Allow-Credentials,Allow-Origin就不能为*,需要指明具体的url域 //httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); //请求方式 httpServletResponse.setHeader("Access-Control-Allow-Methods", "*"); //(预检请求)的返回结果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息) 可以被缓存多久 httpServletResponse.setHeader("Access-Control-Max-Age", "86400"); //首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段 httpServletResponse.setHeader("Access-Control-Allow-Headers", "*"); } //sql,xss过滤 HttpServletRequest httpServletRequest=(HttpServletRequest)request; logger.info("CrosXssFilter.......orignal url:{},ParameterMap:{}",httpServletRequest.getRequestURI(), JSONObject.toJSONString(httpServletRequest.getParameterMap())); XssHttpServletRequestWrapper xssHttpServletRequestWrapper=new XssHttpServletRequestWrapper( httpServletRequest); chain.doFilter(xssHttpServletRequestWrapper, response); logger.info("CrosXssFilter..........doFilter url:{},ParameterMap:{}",xssHttpServletRequestWrapper.getRequestURI(), JSONObject.toJSONString(xssHttpServletRequestWrapper.getParameterMap())); } @Override public void destroy() {
}
}
XssHttpServletRequestWrapper类:
/** * 防止sql注入,xss攻击 * 前端可以对输入信息做预处理,后端也可以做处理。 */ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { private final Logger log = LoggerFactory.getLogger(getClass()); private static String key = "and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+"; private static Set<String> notAllowedKeyWords = new HashSet<String>(0); private static String replacedString="INVALID"; static { String keyStr[] = key.split("\\|"); for (String str : keyStr) { notAllowedKeyWords.add(str); } }
private String currentUrl; public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) { super(servletRequest); currentUrl = servletRequest.getRequestURI(); } /**覆盖getParameter方法,将参数名和参数值都做xss过滤。 * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取 * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖 */ @Override public String getParameter(String parameter) { String value = super.getParameter(parameter); if (value == null) { return null; } return cleanXSS(value); } @Override public String[] getParameterValues(String parameter) { String[] values = super.getParameterValues(parameter); if (values == null) { return null; } int count = values.length; String[] encodedValues = new String[count]; for (int i = 0; i < count; i++) { encodedValues[i] = cleanXSS(values[i]); } return encodedValues; } @Override public Map<String, String[]> getParameterMap(){ Map<String, String[]> values=super.getParameterMap(); if (values == null) { return null; } Map<String, String[]> result=new HashMap<>(); for(String key:values.keySet()){ String encodedKey=cleanXSS(key); int count=values.get(key).length; String[] encodedValues = new String[count]; for (int i = 0; i < count; i++){ encodedValues[i]=cleanXSS(values.get(key)[i]); } result.put(encodedKey,encodedValues); } return result; } /** * 覆盖getHeader方法,将参数名和参数值都做xss过滤。 * 如果需要获得原始的值,则通过super.getHeaders(name)来获取 * getHeaderNames 也可能需要覆盖 */ @Override public String getHeader(String name) { String value = super.getHeader(name); if (value == null) { return null; } return cleanXSS(value); } private String cleanXSS(String valueP) { // You'll need to remove the spaces from the html entities below String value = valueP.replaceAll("<", "&lt;").replaceAll(">", "&gt;"); value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;"); value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;"); value = value.replaceAll("'", "& #39;"); value = value.replaceAll("eval\\((.*)\\)", ""); value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\""); value = value.replaceAll("script", ""); value = cleanSqlKeyWords(value); return value; } private String cleanSqlKeyWords(String value) { String paramValue = value; for (String keyword : notAllowedKeyWords) { if (paramValue.length() > keyword.length() + 4 && (paramValue.contains(" "+keyword)||paramValue.contains(keyword+" ")||paramValue.contains(" "+keyword+" "))) { paramValue = StringUtils.replace(paramValue, keyword, replacedString); log.error(this.currentUrl + "已被过滤,因为参数中包含不允许sql的关键词(" + keyword + ")"+";参数:"+value+";过滤后的参数:"+paramValue); } } return paramValue; }
}