zoukankan      html  css  js  c++  java
  • SpringBoot——防止sql注入,xss攻击

    SpringBoot——防止sql注入,xss攻击

    sql注入:把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。

    解决方法:

    (1)无论是直接使用数据库还是使用如mybatis组件,使用sql的预编译,不要用拼接字符串;

    (2)后台过滤检测:使用正则表达式过滤传入的参数**;**.字符串过滤;

    (4)前端检测sql常见关键字,如or and drop之类的。

    xss攻击:其原理是攻击者向有XSS漏洞的网站中输入(传入)恶意的HTML代码,当其它用户浏览该网站时,这段HTML代码会自动执行,从而达到攻击的目的。

    如,盗取用户Cookie、破坏页面结构、重定向到其它网站等。

    解决方法:对用户输入的表单信息进行检测过滤。

    第一步

    创建XssAndSqlHttpServletRequestWrapper包装器,这是实现XSS过滤的关键,在其内重写了getParameter,getParameterValues,getHeader等方法,对http请求内的参数进行了过滤。

    package com.gannan.court.config;

    import org.springframework.util.StreamUtils;

    import javax.servlet.ReadListener;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import java.io.BufferedReader;
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.*;
    import java.util.regex.Pattern;

    /**
    * @ClassName: XssAndSqlHttpServletRequestWrapper
    * @Description: TODO
    * @Author: zsl
    * @Date: 2020/11/12 14:56
    * @Version: v1.0
    */
    public class XssAndSqlHttpServletRequestWrapper extends HttpServletRequestWrapper {
    HttpServletRequest orgRequest = null;
    private Map<String, String[]> parameterMap;
    private final byte[] body; //用于保存读取body中数据

    public XssAndSqlHttpServletRequestWrapper(HttpServletRequest request) throws IOException{
    super(request);
    orgRequest = request;
    parameterMap = request.getParameterMap();
    body = StreamUtils.copyToByteArray(request.getInputStream());
    }

    // 重写几个HttpServletRequestWrapper中的方法
    /**
    * 获取所有参数名
    *
    * @return 返回所有参数名
    */
    @Override
    public Enumeration<String> getParameterNames() {
    Vector<String> vector = new Vector<String>(parameterMap.keySet());
    return vector.elements();
    }

    /**
    * 覆盖getParameter方法,将参数名和参数值都做xss & sql过滤。<br/>
    * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取<br/>
    * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
    */
    @Override
    public String getParameter(String name) {
    String[] results = parameterMap.get(name);
    if (results == null || results.length <= 0)
    return null;
    else {
    String value = results[0];
    if (value != null) {
    value = xssEncode(value);
    }
    return value;
    }
    }

    /**
    * 获取指定参数名的所有值的数组,如:checkbox的所有数据 接收数组变量 ,如checkobx类型
    */
    @Override
    public String[] getParameterValues(String name) {
    String[] results = parameterMap.get(name);
    if (results == null || results.length <= 0)
    return null;
    else {
    int length = results.length;
    for (int i = 0; i < length; i++) {
    results[i] = xssEncode(results[i]);
    }
    return results;
    }
    }

    /**
    * 覆盖getHeader方法,将参数名和参数值都做xss & sql过滤。<br/>
    * 如果需要获得原始的值,则通过super.getHeaders(name)来获取<br/>
    * getHeaderNames 也可能需要覆盖
    */
    @Override
    public String getHeader(String name) {

    String value = super.getHeader(xssEncode(name));
    if (value != null) {
    value = xssEncode(value);
    }
    return value;
    }

    /**
    * 将容易引起xss & sql漏洞的半角字符直接替换成全角字符
    *
    * @param s
    * @return
    */
    private static String xssEncode(String s) {
    if (s == null || s.isEmpty()) {
    return s;
    } else {
    s = stripXSSAndSql(s);
    }
    StringBuilder sb = new StringBuilder(s.length() + 16);
    for (int i = 0; i < s.length(); i++) {
    char c = s.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;
    }
    }
    return sb.toString();
    }

    /**
    * 获取最原始的request
    *
    * @return
    */
    public HttpServletRequest getOrgRequest() {
    return orgRequest;
    }

    /**
    * 获取最原始的request的静态方法
    *
    * @return
    */
    public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
    if (req instanceof XssAndSqlHttpServletRequestWrapper) {
    return ((XssAndSqlHttpServletRequestWrapper) req).getOrgRequest();
    }
    return req;
    }

    /**
    *
    * 防止xss跨脚本攻击(替换,根据实际情况调整)
    */

    public static String stripXSSAndSql(String value) {
    if (value != null) {
    // NOTE: It's highly recommended to use the ESAPI library and
    // uncomment the following line to
    // avoid encoded attacks.
    // value = ESAPI.encoder().canonicalize(value);
    // Avoid null characters
    /** value = value.replaceAll("", ""); ***/
    // Avoid anything between script tags
    Pattern scriptPattern = Pattern.compile(
    "<[\r\n| | ]*script[\r\n| | ]*>(.*?)</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
    value = scriptPattern.matcher(value).replaceAll("");
    // Avoid anything in a
    // src="http://www.yihaomen.com/article/java/..." type of
    // e-xpression
    scriptPattern = Pattern.compile("src[\r\n| | ]*=[\r\n| | ]*[\\\"|\\\'](.*?)[\\\"|\\\']",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");
    // Remove any lonesome </script> tag
    scriptPattern = Pattern.compile("</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
    value = scriptPattern.matcher(value).replaceAll("");
    // Remove any lonesome <script ...> tag
    scriptPattern = Pattern.compile("<[\r\n| | ]*script(.*?)>",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");
    // Avoid eval(...) expressions
    scriptPattern = Pattern.compile("eval\\((.*?)\\)",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");
    // Avoid e-xpression(...) expressions
    scriptPattern = Pattern.compile("e-xpression\\((.*?)\\)",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");
    // Avoid javascript:... expressions
    scriptPattern = Pattern.compile("javascript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
    value = scriptPattern.matcher(value).replaceAll("");
    // Avoid vbscript:... expressions
    scriptPattern = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
    value = scriptPattern.matcher(value).replaceAll("");
    // Avoid οnlοad= expressions
    scriptPattern = Pattern.compile("onload(.*?)=",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");
    }
    return value;
    }

    /**
    * 审查参数的具体方法
    * @param value
    * @return
    */
    public static boolean checkXSSAndSql(String value) {
    boolean flag = false;
    if (value != null) {
    // NOTE: It's highly recommended to use the ESAPI library and
    // uncomment the following line to
    // avoid encoded attacks.
    // value = ESAPI.encoder().canonicalize(value);
    // Avoid null characters
    /** value = value.replaceAll("", ""); ***/
    // Avoid anything between script tags
    Pattern scriptPattern = Pattern.compile(
    "<[\r\n| | ]*script[\r\n| | ]*>(.*?)</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
    flag = scriptPattern.matcher(value).find();
    if (flag) {
    return flag;
    }
    // Avoid anything in a
    // src="http://www.yihaomen.com/article/java/..." type of
    // e-xpression
    scriptPattern = Pattern.compile("src[\r\n| | ]*=[\r\n| | ]*[\\\"|\\\'](.*?)[\\\"|\\\']",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    flag = scriptPattern.matcher(value).find();
    if (flag) {
    return flag;
    }
    // Remove any lonesome </script> tag
    scriptPattern = Pattern.compile("</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
    flag = scriptPattern.matcher(value).find();
    if (flag) {
    return flag;
    }
    // Remove any lonesome <script ...> tag
    scriptPattern = Pattern.compile("<[\r\n| | ]*script(.*?)>",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    flag = scriptPattern.matcher(value).find();
    if (flag) {
    return flag;
    }
    // Avoid eval(...) expressions
    scriptPattern = Pattern.compile("eval\\((.*?)\\)",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    flag = scriptPattern.matcher(value).find();
    if (flag) {
    return flag;
    }
    // Avoid e-xpression(...) expressions
    scriptPattern = Pattern.compile("e-xpression\\((.*?)\\)",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    flag = scriptPattern.matcher(value).find();
    if (flag) {
    return flag;
    }
    // Avoid javascript:... expressions
    scriptPattern = Pattern.compile("javascript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
    flag = scriptPattern.matcher(value).find();
    if (flag) {
    return flag;
    }
    // Avoid vbscript:... expressions
    scriptPattern = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
    flag = scriptPattern.matcher(value).find();
    if (flag) {
    return flag;
    }
    // Avoid οnlοad= expressions
    scriptPattern = Pattern.compile("onload(.*?)=",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    flag = scriptPattern.matcher(value).find();
    if (flag) {
    return flag;
    }
    scriptPattern = Pattern.compile("\\b(and|exec|insert|select|drop|grant|alter|delete|update|count|chr|mid|master|truncate|char|declare|or)\\b|(\\*|;|\\+|'|%)",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    flag = scriptPattern.matcher(value).find();
    if (flag) {
    return flag;
    }
    }
    return flag;
    }

    /**
    * 检查参数方法
    * @return
    */
    public final boolean checkParameter() {
    Map<String, String[]> submitParams = new HashMap(parameterMap);
    Set<String> submitNames = submitParams.keySet();
    for (String submitName : submitNames) {
    Object submitValues = submitParams.get(submitName);
    if ((submitValues instanceof String)) {
    if (checkXSSAndSql((String) submitValues)) {
    return true;
    }
    } else if ((submitValues instanceof String[])) {
    for (String submitValue : (String[])submitValues){
    if (checkXSSAndSql(submitValue)) {
    return true;
    }
    }
    }
    }
    return false;
    }

    /**
    **application/x- www-form-urlencoded是Post请求默认的请求体内容类型,也是form表单默认的类型。
    * Servlet API规范中对该类型的请求内容提供了request.getParameter()方法来获取请求参数值。
    * 但当请求内容不是该类型时,需要调用request.getInputStream()或request.getReader()方法来获取请求内容值。
    */
    /**
    * 获取请求内容
    * @return
    * @throws IOException
    */
    @Override
    public BufferedReader getReader() throws IOException {
    return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    /**
    * 返回请求内容字节流,多用于文件上传,
    * request.getReader()是对前者返回内容的封装,可以让调用者更方便字符内容的处理(不用自己先获取字节流再做字符流的转换操作)
    * @return
    * @throws IOException
    */
    @Override
    public ServletInputStream getInputStream() throws IOException {
    final ByteArrayInputStream bais = new ByteArrayInputStream(body);
    return new ServletInputStream() {

    @Override
    public int read() throws IOException {
    return bais.read();
    }

    @Override
    public boolean isFinished() {
    // TODO Auto-generated method stub
    return false;
    }

    @Override
    public boolean isReady() {
    // TODO Auto-generated method stub
    return false;
    }

    @Override
    public void setReadListener(ReadListener arg0) {
    // TODO Auto-generated method stub

    }
    };
    }

    }

    第二步

    编写过滤器

    package com.gannan.court.config;

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.PrintWriter;

    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;

    import com.alibaba.fastjson.JSONObject;
    import io.micrometer.core.instrument.util.StringUtils;


    /**
    * @ClassName: XssAndSqlFilter
    * @Description: TODO
    * @Author: zsl
    * @Date: 2020/11/12 14:57
    * @Version: v1.0
    */

    public class XssAndSqlFilter implements Filter {

    @Override
    public void destroy() {
    // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
    String method = "GET";//设置初始值
    String param = "";
    XssAndSqlHttpServletRequestWrapper xssRequest = null;
    if (request instanceof HttpServletRequest) {//判断左边的对象是否是它右边对象的实例
    method = ((HttpServletRequest) request).getMethod();//得到请求URL地址时使用的方法
    xssRequest = new XssAndSqlHttpServletRequestWrapper((HttpServletRequest) request);//创建对象
    }
    if ("POST".equalsIgnoreCase(method)) {//判断是否为post
    param = this.getBodyString(xssRequest.getReader());//获取参数
    if(StringUtils.isNotBlank(param)){//等价于 str != null && str.length > 0 && str.trim().length > 0
    if(xssRequest.checkXSSAndSql(param)){//进行参数审查
    response.setCharacterEncoding("UTF-8");
    response.setContentType("application/json;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.write(JSONObject.toJSONString("您所访问的页面请求中有违反安全规则元素存在,拒绝访问!"));
    return;
    }
    }
    }

    /**
    * 检查参数的时候 同时检查请求的方法
    * 只检查get请求方法和post请求方法的的参数的数据是否合法
    * 并不是所有参数都要检查,首先必须是一个get或者post,再去校验参数
    * 因为PUT方法在进行参数审查的时候没办法通过所以直接过滤掉
    */
    if (xssRequest.checkParameter()&&(method.equals("POST") || method.equals("GET"))){
    response.setCharacterEncoding("UTF-8");
    response.setContentType("application/json;charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.write(JSONObject.toJSONString("您所访问的页面请求中有违反安全规则元素存在,拒绝访问!"));
    return;
    }
    chain.doFilter(xssRequest, response);
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
    // TODO Auto-generated method stub

    }

    // 获取request请求body中参数
    public static String getBodyString(BufferedReader br) {
    String inputLine;
    String str = "";
    try {
    while ((inputLine = br.readLine()) != null) {
    str += inputLine;
    }
    br.close();
    } catch (IOException e) {
    System.out.println("IOException: " + e);
    }
    return str;

    }
    }

    第三步

    在webconfig中注册过滤器XssAndSqlFilter

    package com.demo.config;

    import java.util.Map;

    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    import com.beust.jcommander.internal.Maps;
    import com.swireproperties.cams.common.filter.XssAndSqlFilter;

    /**
    * web配置
    */
    @Configuration
    public class WebConfig {

    @Bean
    public FilterRegistrationBean xssFilterRegistrationBean() {
    //创建一个自定义的 Filter
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
    filterRegistrationBean.setFilter(new XssAndSqlFilter());
    filterRegistrationBean.setOrder(1);
    filterRegistrationBean.setEnabled(true);
    filterRegistrationBean.addUrlPatterns("/*");
    Map<String, String> initParameters = Maps.newHashMap();
    initParameters.put("excludes", "/favicon.ico,/img/*,/js/*,/css/*");
    initParameters.put("isIncludeRichText", "true");
    filterRegistrationBean.setInitParameters(initParameters);
    return filterRegistrationBean;
    }

    }

    备注

    • excludes用于配置不需要参数过滤的请求url。

    • isIncludeRichText默认为true,主要用于设置富文本内容是否需要过滤。

     
     
  • 相关阅读:
    numpy学习(将条件逻辑表述为数组运算)
    numpy学习(利用数组进行数据处理)
    numpy学习(通用函数:快速的元素级数组函数)
    numpy学习(数组转置和轴对换)
    numpy学习(花式索引)
    关于C++中的虚拟继承的一些总结
    adb常用命令
    进程隐藏的方法
    Microsoft Detours 2.1简介
    ebay如何确定同一电脑登陆了多个账号,以及同一账号登陆过多台电脑
  • 原文地址:https://www.cnblogs.com/javalinux/p/15775080.html
Copyright © 2011-2022 走看看