首页 > 其他分享 >工作总结之安全问题篇

工作总结之安全问题篇

时间:2023-01-08 22:55:52浏览次数:40  
标签:总结 return String 工作 Pattern value 安全 replaceAll import

目录

工作总结之安全问题篇

前言

对于我们公司的辣鸡项目还要跑安全测试,真是太抬举它了,谁会去攻击内网的系统?
好了,问题还是要来解决的,测试使用的软件是appscan,会出一个安全报告,告诉开发去改,报告又不说人话,开发是狗啊,开发还真的是狗,上司技术经理跟个傻逼一样,只会说大话,不干实事,瞎逼逼,还打乱自己的思路,一句很简单完事,不知道他是怎么面上来的,看完报告没有啥头绪,于是去百度,去谷歌,寻找各种能解决问题的方法。

解决方法

主要思路就是通过过滤器和拦截器去识别敏感字符(路径或者是传参),要么替换掉敏感字符,要么直接拦截给出403之类的错误码。

具体实现的代码

过滤器

import com.workplat.filter.wrapper.XssHttpRequestWrapper;
import com.workplat.utils.PropertiesUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @program: approval
 * @description: 全局过滤器
 * @author: xinghao
 * @create: 2022-12-05 15:22
 */
public class XssFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("=====过滤器初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        String xssEnabledStr = PropertiesUtils.getPropertyValue("xss.enabled");
        Boolean xssEnabled = Boolean.valueOf(xssEnabledStr);
//        System.out.println("过滤器非bean读取的值:" + xssEnabled);
        if(xssEnabled){
            HttpServletRequest request = (HttpServletRequest)servletRequest;
            HttpServletResponse response = (HttpServletResponse)servletResponse;
            String referer=request.getHeader("referer");
            if(StringUtils.isNotBlank(referer) && !referer.contains(request.getServerName())){
                /**
                 * 如果 链接地址来自其他网站,则返回禁止访问
                 */
                response.setStatus(403);
            }else{
                StringBuffer requestURL = request.getRequestURL();
                if (findUnixChar(requestURL.toString())){
                    response.setStatus(403);
                    return ;
                }
                XssHttpRequestWrapper requestWrapper = new XssHttpRequestWrapper(request);
                if(servletRequest instanceof HttpServletRequest){
                    filterChain.doFilter(requestWrapper, servletResponse);
//                filterChain.doFilter(servletRequest, servletResponse);
                }else {
                    filterChain.doFilter(servletRequest, servletResponse);
                }
            }
        }else {
            filterChain.doFilter(servletRequest,servletResponse);
        }
//        System.out.println("=====过滤器走你");
    }

    @Override
    public void destroy() {
        System.out.println("=====过滤器销毁");
    }

    private Boolean findUnixChar(String value) {
        String unixCharPattern= "\\.\\.";
        Pattern pattern = Pattern.compile(unixCharPattern);
        Matcher matcher = pattern.matcher(value);
        if (matcher.find()){
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }
}

请求参数的包装类

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StreamUtils;
import org.springframework.web.util.ContentCachingRequestWrapper;

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

/**
 * @program: approval
 * @description: XSS攻击过滤器
 * @author: xinghao
 * @create: 2022-12-05 15:04
 */
@Slf4j
public class XssHttpRequestWrapper extends ContentCachingRequestWrapper {

    /**
     * 缓存下来的HTTP body
     */
//    private final String body;

    public XssHttpRequestWrapper(HttpServletRequest servletRequest) throws IOException {
        super(servletRequest);
//        StringBuilder stringBuilder = new StringBuilder();
//        BufferedReader bufferedReader = null;
//        InputStream inputStream = null;
//        try {
//            inputStream = servletRequest.getInputStream();
//            if (inputStream != null) {
//                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
//                char[] charBuffer = new char[128];
//                int bytesRead = -1;
//                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
//                    stringBuilder.append(charBuffer, 0, bytesRead);
//                }
//            } else {
//                stringBuilder.append("");
//            }
//        } catch (IOException ex) {
//
//        } finally {
//            if (inputStream != null) {
//                try {
//                    inputStream.close();
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            }
//            if (bufferedReader != null) {
//                try {
//                    bufferedReader.close();
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            }
//        }
//        body = stringBuilder.toString();
    }

    /**
     * 重新包装输入流
     * @return
     * @throws IOException
     */
//    @Override
//    public ServletInputStream getInputStream() throws IOException {
//        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
//        ServletInputStream servletInputStream = new ServletInputStream() {
//            @Override
//            public boolean isFinished() {
//                return false;
//            }
//
//            @Override
//            public boolean isReady() {
//                return false;
//            }
//
//            @Override
//            public void setReadListener(ReadListener readListener) {
//            }
//
//            @Override
//            public int read() throws IOException {
//                return byteArrayInputStream.read();
//            }
//        };
//        return servletInputStream;
//
//    }
//
//
//    @Override
//    public BufferedReader getReader() throws IOException {
//        return new BufferedReader(new InputStreamReader(this.getInputStream()));
//    }
//
//    public String getBody() {
//        return this.body;
//    }

    @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 String getParameter(String parameter) {

        String value = super.getParameter(parameter);
        if (value == null) {
            return null;

        }
        return cleanXSS(value);

    }

    @Override
    public String getHeader(String name) {

        String value = super.getHeader(name);
        if (value == null) return null;
        return cleanXSS(value);

    }

    private String cleanXSS(String value) {
        if (value == null || "".equals(value)){
            return value;
        }
        value = value.replaceAll(";", "");
        value = value.replaceAll("\"", "");
        value = value.replaceAll("%3C/", "");
        value = value.replaceAll("%3C", "");
        value = value.replaceAll("%3", "");
        value = value.replaceAll("%3E", "");
        value = value.replaceAll("%22%3E%3C", "");
        value = value.replaceAll("%2e%2e", "");
        value = value.replaceAll("win.ini", "");
        value = value.replaceAll("boot.ini", "");
//        value = value.replaceAll("\\.\\.", "");
        value = value.replaceAll("\\|", "");
        //过滤过多导致登录失败
//        value = value.replaceAll("&", "");
        value = value.replaceAll("\\$", "");
        value = value.replaceAll("%", "");
        //过滤过多导致登录失败
//        value = value.replaceAll("@", "");
        value = value.replaceAll("\"", "");
        value = value.replaceAll("\\+", "");
        value = value.replaceAll(",", "");
        // 避免 null 字符
        value = value.replaceAll("", "");
        value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
        value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");//  全角大于号  全角小于号
        value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");// \\(  \\)
        value = value.replaceAll("'", "& #39;");
        // 避免 任何  script 标签
        Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
        value = scriptPattern.matcher(value).replaceAll("");
        // 避免 任何  src="..."
        scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        value = scriptPattern.matcher(value).replaceAll("");
        scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        value = scriptPattern.matcher(value).replaceAll("");
        // 删除任何 </script> 标签
        scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
        value = scriptPattern.matcher(value).replaceAll("");
        // 删除任何 <script ...> 标签
        scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        value = scriptPattern.matcher(value).replaceAll("");
        // 避免 eval(...)
        scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        value = scriptPattern.matcher(value).replaceAll("");
        // 避免 e­xpression(...)
        scriptPattern = Pattern.compile("e­xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        value = scriptPattern.matcher(value).replaceAll("");
        // 避免 javascript:...
        scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
        value = scriptPattern.matcher(value).replaceAll("");
        // 避免 vbscript:...
        scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
        value = scriptPattern.matcher(value).replaceAll("");
        // 避免 onl oad=
        scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        value = scriptPattern.matcher(value).replaceAll("");
        return value;
    }

}

拦截器(写在preHandl方法中)

String xssEnabledStr = PropertiesUtils.getPropertyValue("xss.enabled");
        Boolean xssEnabled = Boolean.valueOf(xssEnabledStr);
//        System.out.println("拦截器非bean读取的值:" + xssEnabled);
        if(xssEnabled){
            // 如果是OPTIONS请求,让其响应一个 200状态码,说明可以正常访问
            if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
                response.setStatus(HttpServletResponse.SC_OK);
                // 放行OPTIONS请求
                return true;
            }
            //获取get请求参数
            Map<String, String[]> parameterMap = request.getParameterMap();
            if (parameterMap.size() > 0){
                for (String key : parameterMap.keySet()) {
                    String value = Arrays.toString(parameterMap.get(key));
                    System.out.println("===get请求参数key:" + key + ", value:" + value);
                    if(stopXss(value)){
                        response.setStatus(403);
                        return false;
                    }
                }
            }
            //获取post请求body
            byte[] bodyBytes = StreamUtils.copyToByteArray(request.getInputStream());
            String body = new String(bodyBytes, request.getCharacterEncoding());
            System.out.println("===post请求体:" + body);
            if (StringUtils.isNotBlank(body) && stopXss(body)){
                response.setStatus(403);
                return false;
            }
            // 拦截攻击字符
            if (StringUtils.isNotBlank(request.getQueryString())){
                String s = request.getQueryString().toLowerCase();
                if (judgeSQLInject(s)){
                    response.setContentType("text/html;charset=UTF-8");
                    response.getWriter().print("参数含有非法攻击字符,已禁止继续访问!");
                    response.setStatus(403);
                    return false;
                }
            }
        }

配置读取类

import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;

@Component
public class PropertiesUtils implements EmbeddedValueResolverAware {
    private static StringValueResolver stringValueResolver;

    @Override
    public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
        PropertiesUtils.stringValueResolver = stringValueResolver;
    }

    public static String getPropertyValue(String propertyName) {
        String finalPropertyName = "${"+propertyName+"}";
        return PropertiesUtils.stringValueResolver.resolveStringValue(finalPropertyName);
    }
}

解释一下部分不好懂的地方

  1. String xssEnabledStr = PropertiesUtils.getPropertyValue("xss.enabled");
    这是为了将是否开启安全过滤开关集成在了配置里面,因为识别的字符太多有时候会影响正常功能,配置一个开关以应付安全问题检查
  2. XssHttpRequestWrapper为什么继承的是ContentCachingRequestWrapper而不是网上看到的大部分的都是HttpServletRequestWrapper
    首先要明白,该Wrapper类有两个作用:一个是重写请求的一些基础方法,getParameter、getParameterValues等,在spring为controller方法的参数解析赋值的时候,底层其实也是调的这些方法,所以重写时就可以加上敏感字符的替换,才能起到作用;另一个作用是重写getInputStream,用于防止参数的二次使用时空指针(就是我们要解决的controller层方法参数为空指针问题),因为根据规范,getInputStream只能被调用一次,第二次就会为空,如果Wrapper类将其缓存起来就能实现多次调用了,最初重写该方法是为了解决post请求体的数据(请求体的数据只能通过getInputStream得到)不能二次读取的问题,但随着了解逐渐深入,发现getParameter这类方法的底层也是getInputStream,所以理论上,如果在过滤器或者是拦截器调了getParameter这类方法(第一次调用),而又没有重写getInputStream,那么controller层的方法参数(第二次调用)仍然会是空指针的情况。结论就是,对于想要在过滤器或者是拦截器使用请求参数的场景,是必须重写getInputStream。
    再次回到实际遇到的问题,这个问题原因在于继承HttpServletRequestWrapper对于controller层的不带类似@RequestParam的参数(就是方法里对于要获取的参数前面什么注解都不写,正常情况下,spring也能自动解析赋值,但我们现在不是正常情况),会是空指针,spring解析不出来,而ContentCachingRequestWrapper是spring提供的,已经帮我们重写过了getInputStream方法,继承它就规避了这个问题。
    ps:我不太确定如果加上注解是不是也能规避这个空指针问题,当时遇到这个问题的时候,方法参数前面正好没有任何注解,根据上面的分析我觉得就算加上也不能规避。
    ps:我想起来了,网上都是继承HttpServletRequestWrapper并且手动重写getInputStream方法能够解决post请求体的数据二次读取的问题,但是对于我遇到的情况:不是json数据,单个参数的场景(无注解,依赖于spring自动解析赋值),是会空指针的,所以需要ContentCachingRequestWrapper
    ps:我隐约记得:无注解,依赖于spring自动解析赋值,底层好像也是调的getParameter(之前好像有看到过相关的文章,应该是的,可以再深入查一查)

标签:总结,return,String,工作,Pattern,value,安全,replaceAll,import
From: https://www.cnblogs.com/xxg98/p/17035650.html

相关文章

  • 12.30-0108文献阅读总结
      本周是论文研读第一周,可是一篇都没有仔细读完,怎样才算仔细研读呢,心里的标准是这样的:  1、从一定程度上区分筛选出好论文;  2、对作者及研究机构分析,在大实验的官......
  • 二叉树递归模板总结
    101.对称二叉树boolisQ(TreeNode*root1,TreeNode*root2){if(root1==nullptr&&root2==nullptr){returntrue;}elseif(roo......
  • GB/T 35279-2017 信息安全技术 云计算安全参考架构 附录下载地址
    声明本文是学习​​GB-T35279-2017信息安全技术云计算安全参考架构.下载地址http://github5.com/view/594​​而整理的学习笔记,分享出来希望更多人受益,如果存在侵权......
  • 工作总结之线程池和原子类篇
    目录工作总结之线程池和原子类篇前言解决思路具体实现代码期间遇到的问题有待优化的地方工作总结之线程池和原子类篇前言起因是,项目里面的同步代码的执行时间过久(20小......
  • BBS登录功能思路总结
    BBS登录功能思路总结目录BBS登录功能思路总结一、登录功能编写步骤概览二、登录功能编写步骤详细1.开设登录接口2.写登录视图函数先返回页面3.创建登录前端页面先渲染......
  • 冰面厚度安全警示 All In One
    冰面厚度安全警示AllInOne天气预报<10cm⚠️>10cm......
  • 2023年1月2日到8日文化课学习总结
    主要是懒得动笔写了,浪费纸张,索性就在博客上写吧!并且星期天睡了半天了,头脑晕乎乎的。主要学科有:语文,数学,英语三大基础学科+物理,化学,生物选科 总结如下:(分科整理的)语文......
  • P14_协同工作-开发者的权限说明以及如何维护项目成员
    不同项目成员对应的权限开发者的权限说明开发者权限:可使用小程序开发者工具及对小程序的功能进行代码开发体验者权限:可使用体验版小程序登录权限:可登录小程序管理后......
  • P14_协同工作-开发者的权限说明以及如何维护项目成员
    不同项目成员对应的权限开发者的权限说明开发者权限:可使用小程序开发者工具及对小程序的功能进行代码开发体验者权限:可使用体验版小程序登录权限:可登录小程序管理后......
  • P14_协同工作-开发者的权限说明以及如何维护项目成员
    不同项目成员对应的权限开发者的权限说明开发者权限:可使用小程序开发者工具及对小程序的功能进行代码开发体验者权限:可使用体验版小程序登录权限:可登录小程序管理后......