IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Java过滤XSS脚本攻击记录一下 -> 正文阅读

[网络协议]Java过滤XSS脚本攻击记录一下

作者:_335

背景

  • 之前公司信息安全部门对公司项目进行网络安全升级时,发现项目里可能会出现XSS脚本攻击漏洞,所以就需要对其参数进行过滤拦截。

XSS

  • 百度百科:XSS攻击全称:cross site scripting(这里是为了和CSS区分,所以叫XSS),跨站脚本攻击(XSS),是最普遍的Web应用安全漏洞。这类漏洞能够使得攻击者嵌入恶意脚本代码到正常用户会访问到的页面中,当正常用户访问该页面时,则可导致嵌入的恶意脚本代码的执行,从而达到恶意攻击用户的目的。攻击者可以使用户在浏览器中执行其预定义的恶意脚本,其导致的危害可想而知,如劫持用户会话,插入恶意内容、重定向用户、使用恶意软件劫持用户浏览器、繁殖XSS蠕虫,甚至破坏网站、修改路由器配置信息等。
  • xss漏洞攻击分为三种:
    • 反射性XSS攻击:前端在发送请求时,在url参数里携带一些脚本命令,然后等服务端把脚本在反射给浏览器执行脚本代码,进行XSS漏洞攻击
    • 存储性XSS攻击:和反射性XSS漏洞攻击相似,但是服务器会进行持久化保存脚本命令,后续用户访问该数据时持久性进行XSS漏洞攻击
    • DOS性XSS攻击:和服务端没有交互,靠浏览器的DOM解析进行XSS攻击

Java过滤

  • 预防XSS漏洞攻击除了web端进行解析过滤外,也还需要服务端进行校验过滤
  • 本次使用springboot项目中过滤器进行对前端传来的参数进行过滤拦截
/**
 * springboot注册过滤器
 *
 * @author: zrh
 * @date: 2021-11-17
 */

@Configuration
public class XssFilterConfig {

    @Bean
    public FilterRegistrationBean xssFilterRegistrationBean () {
        FilterRegistrationBean initXssFilterBean = new FilterRegistrationBean();
        // 设置自定义过滤器
        initXssFilterBean.setFilter(new XssFilter());
        // 设置优先级(值越低,优先级越高)
        initXssFilterBean.setOrder(1);
        // 设置过滤路径
        initXssFilterBean.addUrlPatterns("/*");
        // 设置过滤器名称
        initXssFilterBean.setName("XSS_filter");
        // 设置过滤器作用范围(可以配置多种,这里指定过滤请求资源)
        initXssFilterBean.setDispatcherTypes(DispatcherType.REQUEST);
        return initXssFilterBean;
    }
}
  • spring项目中可以使用web.xml定义过滤器,springboot中没有web.xml配置文件,那么就可以使用FilterRegistrationBean类把过滤器实例注册到容器
/**
 * 自定义过滤器
 *
 * @author: zrh
 * @date: 2021-11-17
 */
@Slf4j
public class XssFilter implements Filter {

    @Override
    public void init (FilterConfig filterConfig) {
        // 初始化调用
    }

    @Override
    public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        final XssHttpServletRequestWrapper requestWrapper = new XssHttpServletRequestWrapper((HttpServletRequest) servletRequest);
        filterChain.doFilter(requestWrapper, servletResponse);
    }

    @Override
    public void destroy () {
        // 销毁调用
    }
}
/**
 * 重写请求参数过滤
 *
 * @author: zrh
 * @date: 2021-11-17
 */
@Slf4j
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

    public XssHttpServletRequestWrapper (HttpServletRequest request) {
        super(request);
    }

    /**
     * 对GET请求中参数进行过滤校验
     *
     * @param name
     * @return
     */
    @Override
    public String[] getParameterValues (String name) {
        String[] values = super.getParameterValues(name);
        if (values == null) {
            return null;
        }
        int count = values.length;
        String[] cleanParams = new String[count];
        for (int i = 0; i < count; i++) {
            cleanParams[i] = String.valueOf(XssUtil.filterParam(values[i]));
            log.info("getParameterValues -> name:{},过滤前参数:{},过滤后参数:{}", name, values[i], cleanParams[i]);
        }
        return cleanParams;
    }

    /**
     * 对POST请求头进行参数过滤校验
     *
     * @param header
     * @return
     */
    @Override
    public Enumeration getHeaders (String header) {
        final String value = super.getHeader(header);
        final LinkedList list = new LinkedList();
        if (value != null) {
            final Object param = XssUtil.filterParam(value);
            list.addFirst(param);
            log.info("getHeaders -> header:{},过滤前参数:{},过滤后参数:{}", header, value, param);
        }
        return Collections.enumeration(list);
    }

    /**
     * 对POST请求中body参数进行校验
     *
     * @return
     * @throws IOException
     */
    @Override
    public ServletInputStream getInputStream () throws IOException {
        final ByteArrayInputStream stream = new ByteArrayInputStream(inputHandlers(super.getInputStream()).getBytes());
        return new ServletInputStream() {
            @Override
            public int read () {
                return stream.read();
            }

            @Override
            public boolean isFinished () {
                return false;
            }

            @Override
            public boolean isReady () {
                return false;
            }

            @Override
            public void setReadListener (ReadListener readListener) {
            }
        };
    }

    /**
     * 解析请求流参数
     *
     * @param servletInputStream
     * @return
     */
    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) {
            log.error("异常 e:", e);
        } finally {
            if (servletInputStream != null) {
                try {
                    servletInputStream.close();
                } catch (IOException e) {
                    log.error("servletInputStream 关闭异常 e:", e);
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    log.error("reader 关闭异常 e:", e);
                }
            }
        }
        final String param = XssUtil.filterBody(sb.toString());
        log.info("getInputStream -> 过滤前参数:{},过滤后参数:{}", sb, param);
        return param;
    }
}
  • 重写HttpServletRequestWrapper类用于过滤改变请求参数值
/**
 * 参数校验工具类
 *
 * @author: zrh
 * @date: 2021-11-17
 */
@Slf4j
public final class XssUtil {

    private XssUtil () {
    }

    /**
     * 网上找的XSS匹配正则表达式
     */
    private final static Pattern[] PATTERNS = new Pattern[]{
            // Script fragments
            Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE),
            // src='...'
            Pattern.compile("src[\r\n]*=[\r\n]*\'(.*?)\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("src[\r\n]*=[\r\n]*\"(.*?)\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // lonely script tags
            Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),
            Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // eval(...)
            Pattern.compile("eval\((.*?)\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // expression(...)
            Pattern.compile("expression\((.*?)\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // javascript:...
            Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
            // vbscript:...
            Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
            // 空格英文单双引号
            Pattern.compile("[\s'"]+", Pattern.CASE_INSENSITIVE),
            // onload(...)=...
            Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // alert
            Pattern.compile("alert(.*?)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("<", Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile(">", Pattern.MULTILINE | Pattern.DOTALL),
            //Checks any html tags i.e. <script, <embed, <object etc.
            Pattern.compile("(<(script|iframe|embed|frame|frameset|object|img|applet|body|html|style|layer|link|ilayer|meta|bgsound))")
    };

    /**
     * 对请求对象参数进行过滤校验
     *
     * @param params
     * @return
     */
    public static String filterBody (String params) {
        try {
            if (StringUtils.isBlank(params)) {
                return params;
            }
            final Map<String, Object> map = JSONObject.parseObject(params, Map.class);
            if (map.isEmpty()) {
                return params;
            }

            // 参数过滤
            final Iterator<Map.Entry<String, Object>> iterator = map.entrySet().stream().iterator();
            while (iterator.hasNext()) {
                final Map.Entry<String, Object> next = iterator.next();
                next.setValue(filterParam(next.getValue()));
            }
            return JSON.toJSONString(map);
        } catch (Exception e) {
            log.error("XSS过滤异常:", e);
        }
        return params;
    }

    /**
     * 对请求字符串参数进行过滤校验
     *
     * @param param
     * @param <T>
     * @return
     */
    public static <T> Object filterParam (T param) {
        if (param instanceof String) {
            try {
                String value = String.valueOf(param);
                for (Pattern pattern : PATTERNS) {
                    value = pattern.matcher(value).replaceAll("");
                }
                return value;
            } catch (Exception e) {
                log.error("XSS参数过滤异常:", e);
            }
        }
        return param;
    }
}
  • XSS过滤参数的正则工具类
/**
 *
 * @Author: ZRH
 * @Date: 2021/11/17
 */
@RestController
public class XssTest {

    @PostMapping("/xss/test")
    public String test (@RequestBody JSONObject jsonObject) {
        System.out.println(jsonObject.toJSONString());
        return "OK";
    }

    @GetMapping("/xss/test")
    public String test (@RequestParam Integer data, @RequestParam String result) {
        System.out.println(data);
        return "OK";
    }
}

模拟请求响应结果:
17:07:06.597 - [http-nio-8888-exec-1] - getHeaders -> header:content-type,过滤前参数:application/json,过滤后参数:application/json
17:07:06.598 - [http-nio-8888-exec-1] - getHeaders -> header:token,过滤前参数:123,过滤后参数:123
17:07:06.598 - [http-nio-8888-exec-1] - getHeaders -> header:a,过滤前参数:123,过滤后参数:123
17:07:06.598 - [http-nio-8888-exec-1] - getHeaders -> header:b,过滤前参数:<script>alert("XSS");</script>,过滤后参数:
17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:content-length,过滤前参数:67,过滤后参数:67
17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:host,过滤前参数:localhost:8888,过滤后参数:localhost:8888
17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:connection,过滤前参数:Keep-Alive,过滤后参数:Keep-Alive
17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:user-agent,过滤前参数:Apache-HttpClient/4.5.12 (Java/11.0.8),过滤后参数:Apache-HttpClient/4.5.12(Java/11.0.8)
17:07:06.599 - [http-nio-8888-exec-1] - getHeaders -> header:accept-encoding,过滤前参数:gzip,deflate,过滤后参数:gzip,deflate
17:07:06.648 - [http-nio-8888-exec-1] - getInputStream -> 过滤前参数:{  "a": "1",  "b": 2,  "c": "<script>alert(\"XSS\");</script>"},过滤后参数:{"a":"1","b":2,"c":""}
{"a":"1","b":2,"c":""}

最后

  • 除了可以使用过滤器以外,还可以使用拦截器进行拦截校验。大致流程也差不多,先获取参数,然后进行校验,最后重新赋值。
  • 上述代码例子只是简单粗化版,在实际项目中要根据需求进行代码调整和性能优化后才可在线上使用。
  • 虚心学习,共同进步 -_-
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-05-14 10:12:53  更:2022-05-14 10:13:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 0:33:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码