一、过滤器和拦截器的区别:
不同点:
过滤器的执行顺序在拦截前
过滤器基于servlet,而拦截器是基于框架,springmvc
过滤器是基于函数回调,而拦截器是基于Java的反射机制
参考博客:https://blog.csdn.net/zxd1435513775/article/details/80556034
二、基于过滤器解决xss问题
(1)、什么是xss问题
xss问题就是脚本攻击,是指在参数中携带一些script脚本等能在HTML执行的脚本,从而呈现在html页面的脚本被执行导致的结果
(2)解决xss问题 使用过滤器,对每个参数进行参数转化
代码:test1部分
package com.junlaninfo.filter; import org.apache.commons.lang3.StringEscapeUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; /** * Created by 辉 on 2020/6/5. 自定义拦截器 */ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { public XssHttpServletRequestWrapper(HttpServletRequest request) { super(request); } @Override public String getParameter(String name) { String oldValue = super.getParameter(name); if (oldValue == null) { return oldValue; } String s = StringEscapeUtils.escapeHtml4(oldValue); return s ; } }
package com.junlaninfo.filter; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * Created by 辉 on 2020/6/5. 注入自定义拦截器 */ @Component @WebFilter public class TestFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) servletRequest), servletResponse); } @Override public void destroy() { } }
三、自定义注解+aop+反射解决xss问题
代码:test2部分
(1)、自定义注解,定义三个注解,分别作用在方法上、参数上、属性上
package com.junlaninfo.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Description: 注解在方法上,表示该方法中的参数需要预防XSS */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface PreventXSSMethod { }
package com.junlaninfo.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Description: 注解在参数上,表示该参数需要预防XSS */ @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface PreventXSSParameter { }
package com.junlaninfo.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Description: 注解在属性上,表示该字段需要预防XSS */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface PreventXSSField { }
(2)aop
package com.junlaninfo.aop; import com.junlaninfo.annotation.PreventXSSField; import com.junlaninfo.annotation.PreventXSSMethod; import com.junlaninfo.annotation.PreventXSSParameter; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.annotation.Annotation; import java.lang.reflect.Field; /** * @Description: AOP防XSS攻击 */ @Aspect @Component public class PreventXSSAspect { /** * 拦截controller路径下所有类的所有方法 */ @Pointcut("execution(* com.junlaninfo.controller.*.*(..))") public void pointCut() {} @Around("pointCut() && @annotation(preventXSSMethod)") public Object around(ProceedingJoinPoint joinPoint, PreventXSSMethod preventXSSMethod) throws Throwable { Object[] args = joinPoint.getArgs(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 获取所有参数上的注解(一个参数可能对应多个注解,因此获取到的是一个二维数组。) Annotation[][] paramsAnnotations = signature.getMethod().getParameterAnnotations(); // 遍历一维数组,获取每个参数对应的注解数组 for (int i = 0; i < paramsAnnotations.length; i++) { Annotation[] paramAnnotations = paramsAnnotations[i]; // 遍历每个参数的注解数组 for (Annotation annotation: paramAnnotations) { // 如果参数需要预防XSS攻击 if (annotation instanceof PreventXSSParameter){ // 如果是String类型,将其进行格式化 // 否则获取该类型的所有字段,对String类型的字段进行格式化 if (args[i] instanceof String && StringUtils.isNotEmpty((String) args[i])) { args[i] = format((String) args[i]); } else { Class clazz = args[i].getClass(); // 获取类的所有字段 Field[] fields = clazz.getDeclaredFields(); for (Field field: fields) { // 如果字段上有@PreventXSSField注解 if (field.getDeclaredAnnotation(PreventXSSField.class) != null) { // 如果是字段不可访问,设置临时可访问 if (!field.isAccessible()) field.setAccessible(true); // 如果字段是字符串类型则进行格式化 Object fieldValue = field.get(args[i]); if (fieldValue instanceof String && StringUtils.isNotEmpty((String) fieldValue)) field.set(args[i], format((String) fieldValue)); } } } } } } // 将参数覆盖到到原方法 Object proceed = joinPoint.proceed(args); return proceed; } /** * 对需要防范的字符串进行格式化 */ public String format(String xssStr) { return StringEscapeUtils.escapeHtml4(xssStr); } }
(2)entity
package com.junlaninfo.entity; import com.junlaninfo.annotation.PreventXSSField; /** * Created by 辉 on 2020/6/5. */ public class user { private String username; private String password; @PreventXSSField private String nickname; @PreventXSSField private String description; public user() { } public user(String username, String password, String nickname, String description) { this.username = username; this.password = password; this.nickname = nickname; this.description = description; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
(3)controller层
package com.junlaninfo.controller; import com.junlaninfo.*; import com.junlaninfo.annotation.PreventXSSMethod; import com.junlaninfo.annotation.PreventXSSParameter; import com.junlaninfo.entity.user; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; /** * Created by 辉 on 2020/6/5. */ @Controller public class indecController { @GetMapping("/") public String register() { return "register"; } @PreventXSSMethod @PostMapping("/info") public String info(@PreventXSSParameter user user, Model model) { model.addAttribute("user", user); return "user"; } }
(4)ftl
register.ftl
<html> <head> <title>注册页面</title> </head> <body> <form action="/info" method="post"> 用户名:<input type="text" name="username"> 密码:<input type="text" name="password"> 昵称:<input type="text" name="nickname"> 描述:<input type="text" name="description"> <input type="submit" value="提交"> </form> </body> </html>
user.ftl
<html> <head> <title>用户展示页面</title> </head> <body> <p>用户名:${user.username}</p> <p>密码:${user.password}</p> <p>昵称:${user.nickname}</p> <p>描述:${user.description}</p></body> </html>
注意:测试过,我的理解是xss攻击只存在不是前后端分离的项目中,真正前后端分离的项目是不存在xss问题,因为返回的数据都是json格式,有不同的见解欢迎指正
四、使用过滤器解决SQL注入的问题
(1)什么是SQL注入
就是用户在输入内容的时候,在SQL拼接的过程中,导致变成了另外一条SQL语句,且新的SQL语句即使输入的内容不正确,但是这条SQL语句是可以正常执行的
例如:
原SQL语句拼接为:
String sql = "select id from user15 where username='"+user.getUserName()+"' and password='"+user.getPassWord()+"'";
用户输入的帐号密码分别为:
请输入您的帐号:
suibian
请输入您的密码:
suibian' or '1'='1
组成的SQL语句:
select id from user15 where username='suibian' and password='suibian' or '1'='1'
(2)SQL注入问题的解决
使用filter过滤器
预编译
(3)使用filter解决SQL注入的问题
代码:test3
package com.jualninfo.filter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) { super(servletRequest); } 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; } /* 覆盖getParameter方法,将参数名和参数值都做xss过滤。 * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取 * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖 */ public String getParameter(String parameter) { String value = super.getParameter(parameter); if (value == null) { return null; } return cleanXSS(value); } /** * 覆盖getHeader方法,将参数名和参数值都做xss过滤。 * 如果需要获得原始的值,则通过super.getHeaders(name)来获取 * getHeaderNames 也可能需要覆盖 */ public String getHeader(String name) { String value = super.getHeader(name); if (value == null) return null; return cleanXSS(value); } private String cleanXSS(String value) { //You'll need to remove the spaces from the html entities below 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 = value.replaceAll("[*]","["+"*]"); value = value.replaceAll("[+]","["+"+]"); value = value.replaceAll("[?]","["+"?]"); // replace sql 这里可以自由发挥 String[] values = value.split(" "); String badStr = "'|and|exec|execute|insert|select|delete|update|count|drop|%|chr|mid|master|truncate|" + "char|declare|sitename|net user|xp_cmdshell|;|or|-|+|,|like'|and|exec|execute|insert|create|drop|" + "table|from|grant|use|group_concat|column_name|" + "information_schema.columns|table_schema|union|where|select|delete|update|order|by|count|" + "chr|mid|master|truncate|char|declare|or|;|-|--|,|like|//|/|%|#"; String[] badStrs = badStr.split("\\|"); for (int i = 0;i<badStrs.length;i++){ for (int j = 0;j<values.length;j++){ if (values[j].equalsIgnoreCase(badStrs[i])){ values[j] = "forbid"; } } } StringBuilder sb = new StringBuilder(); for (int i = 0;i<values.length;i++){ if (i == values.length-1){ sb.append(values[i]); } else { sb.append(values[i]+" "); } } value = sb.toString(); return value; } }
package com.jualninfo.filter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @WebFilter(urlPatterns = "/*") @Component public class XssFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(XssFilter.class); FilterConfig filterConfig = null; @Override public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } @Override public void destroy() { this.filterConfig = null; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { logger.info("自定义过滤器->doFilter"); chain.doFilter(new XssHttpServletRequestWrapper( (HttpServletRequest) request), response); } }
(4)预编译
在传入参数的时候,不要使用 $(),使用#(),预编译
代码链接:https://gitee.com/xuexionghui/SecurityTest.git