SpringBoot实现防止XSS攻击的示例详解
作者:H愚公移山H
XSS
跨站脚本工具(cross 斯特scripting),为不和层叠样式表(cascading style sheets,CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往web页面里插入恶意ScriptScript代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。防止XSS攻击简单的预防就是对Request请求中的一些参数去掉一些比较敏感的脚本命令。原本是打算通过SpringMVC的HandlerInterceptor机制来实现的,通过获取request然后对request中的参数进行修改,结果虽然值修改了,但在Controller中获取的数值还是没有修改的。没办法就是要Filter来完成。简单来说就是创建一个新的HttpRequest类XssHttpServletRequestWrapper,然后重写一些get方法(获取参数时对参数进行XSS判断预防)
流程梳理
包装request->创建过滤器->添加过滤器
1.创建包装request的类 XssHttpServletRequestWrapper
import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sysware.framework.exceptions.SyswareRuntimeException; import com.sysware.framework.file.service.impl.FileGridServiceImpl; public class XssHttpServletRequestWraper extends HttpServletRequestWrapper { private Logger log = LoggerFactory.getLogger(FileGridServiceImpl.class); public XssHttpServletRequestWraper() { super(null); } public XssHttpServletRequestWraper(HttpServletRequest httpservletrequest) { super(httpservletrequest); } //过滤springmvc中的 @RequestParam 注解中的参数 public String[] getParameterValues(String s) { String str[] = super.getParameterValues(s); if (str == null) { return null; } int i = str.length; String as1[] = new String[i]; for (int j = 0; j < i; j++) { //System.out.println("getParameterValues:"+str[j]); as1[j] = cleanXSS(cleanSQLInject(str[j])); } log.info("XssHttpServletRequestWraper净化后的请求为:==========" + as1); return as1; } //过滤request.getParameter的参数 public String getParameter(String s) { String s1 = super.getParameter(s); if (s1 == null) { return null; } else { String s2 = cleanXSS(cleanSQLInject(s1)); log.info("XssHttpServletRequestWraper净化后的请求为:==========" + s2); return s2; } } //过滤请求体 json 格式的 @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(inputHandlers(super.getInputStream ()).getBytes ()); return new ServletInputStream() { @Override public int read() throws IOException { return bais.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } public String inputHandlers(ServletInputStream servletInputStream){ StringBuilder sb = new StringBuilder(); BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(servletInputStream, Charset.forName("UTF-8"))); String line = ""; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { if (servletInputStream != null) { try { servletInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return cleanXSS(sb.toString ()); } public String cleanXSS(String src) { String temp = src; src = src.replaceAll("<", "<").replaceAll(">", ">"); src = src.replaceAll("\\(", "(").replaceAll("\\)", ")"); src = src.replaceAll("'", ""); src = src.replaceAll(";", ""); /**-----------------------start--------------------------*/ src = src.replaceAll("<", "& lt;").replaceAll(">", "& gt;"); src = src.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41"); src = src.replaceAll("eval\\((.*)\\)", ""); src = src.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\""); // src = src.replaceAll("script", ""); // src = src.replaceAll("link", ""); // src = src.replaceAll("frame", ""); /**-----------------------end--------------------------*/ Pattern pattern = Pattern.compile("(eval\\((.*)\\)|script)", Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(src); src = matcher.replaceAll(""); pattern = Pattern.compile("[\\\"\\'][\\s]*javascript:(.*)[\\\"\\']", Pattern.CASE_INSENSITIVE); matcher = pattern.matcher(src); src = matcher.replaceAll("\"\""); // 增加脚本 src = src.replaceAll("script", "").replaceAll(";", "") /*.replaceAll("\"", "").replaceAll("@", "")*/ .replaceAll("0x0d", "").replaceAll("0x0a", ""); // if (!temp.equals(src)) { // // System.out.println("输入信息存在xss攻击!"); // // System.out.println("原始输入信息-->" + temp); // // System.out.println("处理后信息-->" + src); // // log.error("xss攻击检查:参数含有非法攻击字符,已禁止继续访问!!"); // log.error("原始输入信息-->" + temp); // // throw new SyswareRuntimeException("xss攻击检查:参数含有非法攻击字符,已禁止继续访问!!"); // } return src; } //输出 public void outputMsgByOutputStream(HttpServletResponse response, String msg) throws IOException { ServletOutputStream outputStream = response.getOutputStream(); //获取输出流 response.setHeader("content-type", "text/html;charset=UTF-8"); //通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码 byte[] dataByteArr = msg.getBytes("UTF-8");// 将字符转换成字节数组,指定以UTF-8编码进行转换 outputStream.write(dataByteArr);// 使用OutputStream流向客户端输出字节数组 } // 需要增加通配,过滤大小写组合 public String cleanSQLInject(String src) { String lowSrc = src.toLowerCase(); String temp = src; String lowSrcAfter = lowSrc.replaceAll(" insert ", "forbidI") .replaceAll(" select ", "forbidS") .replaceAll(" update ", "forbidU") .replaceAll(" delete ", "forbidD").replaceAll(" and ", "forbidA") .replaceAll(" or ", "forbidO").replace("'", ""); if (!lowSrcAfter.equals(lowSrc)) { log.error("sql注入检查:输入信息存在SQL攻击!"); log.error("原始输入信息-->" + temp); log.error("处理后信息-->" + lowSrc); throw new SyswareRuntimeException("sql注入检查:参数含有非法攻击字符,已禁止继续访问!!"); } return src; } }
注意:
getInputStream()方法的流处理,注解方式获取数据貌似是根据这个流取得的数据。
因为super.getInputStream()流只允许读取一次,所以在getInputStream()方法中处理完流数据后返回了一个新的ServletInputStream。另外替换方法里的替换规则,也可以根据实际业务需要进行调整。
2.创建过滤器 XssFilter
@Component public class XssFilter implements Filter { // 忽略权限检查的url地址 private final String[] excludeUrls = new String[]{ "/api/item","api/document" }; public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) arg0; HttpServletResponse response = (HttpServletResponse) arg1; String pathInfo = req.getPathInfo() == null ? "" : req.getPathInfo(); //获取请求url的后两层 String url = req.getServletPath() + pathInfo; //获取请求你ip后的全部路径 String uri = req.getRequestURI(); //注入xss过滤器实例 XssHttpServletRequestWraper reqW = new XssHttpServletRequestWraper(req); //过滤掉不需要的Xss校验的地址 for (String str : excludeUrls) { if (uri.indexOf(str) >= 0) { //过滤 arg2.doFilter(reqW, response); return; } } arg2.doFilter(arg0, response); } public void destroy() { } public void init(FilterConfig filterconfig1) throws ServletException { } }
到此这篇关于SpringBoot实现防止XSS攻击的示例详解的文章就介绍到这了,更多相关SpringBoot防止XSS攻击内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!