系统设计-文本内容保存之XSS过滤

news2025/7/10 9:29:30

点击上方名片关注我,为你带来更多踩坑案例

afda49b670c8d62491b0c0094eb499d5.png

- 引言 -

    如果你是一个摸爬滚打几年的开发者,那么这个阶段,对系统设计的合理性绝对是衡量一个人水平的重要标准。

    一个好的设计不光能让你工作中避免很多麻烦,还能为你面试的时候增加很多谈资

    而且,不同设计之间理念都是有借鉴性参考性的,你见过的设计多了,思考的多了,再次面临一个问题的时候,就会有很多点子不由自主的冒出来。

    希望这个系列的文章,能够和大家互相借鉴参考,共同进步。

- XSS攻击 -

    XSS跨站脚本攻击(Cross Site Scripting)攻击就是指在网页中通过一些巧妙的方式,注入一些恶意的可执行代码片段等,来非法攻击网站

    XSS攻击的方式有很多很多,需要我们在开发环节中的每一个步骤都注意

    本文主要介绍一种防止XSS攻击的方法,也是普适性比较高,大家比较容易忽略的一点---服务端保存前端传来的参数,特别是字符串类型的参数,要秉持着不信任的原则,防止把跨站脚本持久化到数据库中

- XSS过滤 -

    方法就是在接收参数前,对参数中的String类型的字段做一个筛查,大致思路分为三步:

    1.拦截到参数--使用过滤器

    2.将参数中的Srting字段拆出来---通过类型判断和遍历,因为有些参数的格式可能会略复杂

    3.将String类型的字段做统一替换---可以使用一些现成的正则替换工具

    好了,思路就是上面的三步,下面直接贴代码

    首先定义我们的过滤器

public class XssFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) {
    }


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


    @Override
    public void destroy() {
    }
}

    然后注册我们的过滤器

@Configuration
public class XSSFilterRegister {
    @Bean
    public FilterRegistrationBean<XssFilter> registerXss() {
        //通过FilterRegistrationBean实例设置优先级可以生效
        FilterRegistrationBean<XssFilter> bean = new FilterRegistrationBean<>();
        //注册自定义过滤器
        bean.setFilter(new XssFilter());
        //过滤器名称
        bean.setName("xssFilter");
        //过滤所有路径
        bean.addUrlPatterns("/*");
        return bean;
    }
}

    最后写过滤器具体要做的事

public class XssHttpRequestWrapper extends HttpServletRequestWrapper {
    public XssHttpRequestWrapper(HttpServletRequest request) {
        super(request);
    }


    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        if (!StringUtils.isEmpty(value)) {
            value = HtmlUtil.filter(value);
        }
        return value;
    }


    @Override
    public String[] getParameterValues(String name) {
        String[] values = super.getParameterValues(name);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                String value = values[i];
                if (!StringUtils.isEmpty(value)) {
                    value = HtmlUtil.filter(value);
                }
                values[i] = value;
            }
        }
        return values;
    }


    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> parameters = super.getParameterMap();
        Map<String, String[]> map = new LinkedHashMap<>();
        if (parameters != null) {
            for (String key : parameters.keySet()) {
                String[] values = parameters.get(key);
                for (int i = 0; i < values.length; i++) {
                    String value = values[i];
                    if (!StringUtils.isEmpty(value)) {
                        value = HtmlUtil.filter(value);
                    }
                    values[i] = value;
                }
                map.put(key, values);
            }
        }
        return map;
    }


    @Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        if (!StringUtils.isEmpty(value)) {
            value = HtmlUtil.filter(value);
        }
        return value;
    }


    @Override
    public ServletInputStream getInputStream() throws IOException {
        InputStream in = super.getInputStream();
        InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8);
        BufferedReader buffer = new BufferedReader(reader);
        StringBuilder body = new StringBuilder();
        String line = buffer.readLine();
        while (line != null) {
            body.append(line);
            line = buffer.readLine();
        }
        buffer.close();
        reader.close();
        in.close();
        Map<String, Object> map = new HashMap<>();
        String json = "";
        try {
            if (body.toString().startsWith("[")) {
                JSONArray jsonArray = JSONUtil.parseArray(body.toString());
                JSONArray newArray = new JSONArray();
                for (Object object : jsonArray) {
                    if (object instanceof Integer) {
                        json = jsonArray.toString();
                        break;
                    } else if (object instanceof Long) {
                        json = jsonArray.toString();
                        break;
                    } else {
                        map = (JSONObject) object;
                        Map<String, Object> result = new LinkedHashMap<>();
                        for (String key : map.keySet()) {
                            filter(result, key, map.get(key));
                        }
                        newArray.add(result);
                    }
                }
                if (newArray.size() != 0) {
                    json = newArray.toString();
                }
            } else if (body.toString().startsWith("{")) {
                map = JSONUtil.parseObj(body.toString());
                if (!map.isEmpty()) {
                    Map<String, Object> result = new LinkedHashMap<>();
                    for (String key : map.keySet()) {
                        filter(result, key, map.get(key));
                    }
                    json = JSONUtil.toJsonStr(result);
                }
            }
        } catch (Exception e1) {
            throw new ParamException(e1);
        }
        ByteArrayInputStream bain = new ByteArrayInputStream(json.getBytes());
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }


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


            @Override
            public void setReadListener(ReadListener readListener) {


            }


            @Override
            public int read() {
                return bain.read();
            }
        };
    }


    private static void filter(Map<String, Object> result, String key, Object val) {
        if (val instanceof String) {
            // 字符串直接替换
            result.put(key, HtmlUtil.filter(val.toString()));
        } else if (val instanceof JSONObject) {
            // JSONObject遍历
            JSONObject valObj = new JSONObject(val);
            JSONObject newObj = new JSONObject();
            for (String keyObj : valObj.keySet()) {
                if (valObj.get(keyObj) instanceof String && !StringUtils.isEmpty(valObj.get(keyObj).toString())) {
                    newObj.set(keyObj, HtmlUtil.filter(valObj.get(keyObj).toString()));
                } else {
                    newObj.set(keyObj, valObj.get(keyObj));
                }
            }
            result.put(key, newObj);
        } else if (val instanceof JSONArray) {
            // JSONArray遍历
            JSONArray valArray = new JSONArray(val);
            JSONArray newArray = new JSONArray();
            for (Object o : valArray) {
                if (o instanceof JSONObject) {
                    JSONObject valObj = (JSONObject) o;
                    JSONObject newObj = new JSONObject();
                    for (String keyObj : valObj.keySet()) {
                        if (valObj.get(keyObj) instanceof String && !StringUtils.isEmpty(valObj.get(keyObj).toString())) {
                            newObj.set(keyObj, HtmlUtil.filter(valObj.get(keyObj).toString()));
                        } else {
                            newObj.set(keyObj, valObj.get(keyObj));
                        }
                    }
                    newArray.put(newObj);
                } else {
                    newArray.put(o);
                }
            }
            result.put(key, newArray);
        } else {
            // 其他类型字段不做替换
            result.put(key, val);
        }
    }
}

    其中的一些类大家可以做自行替换,比如正则过滤的工具类我使用的是hutool的工具包HtmlUtil,大家也可以根据业务场景的不同用别的或者自定义。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/36774.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【科学文献计量】RC.networkMultiLevel()中的参数解释

RC.networkMultiLevel中的参数解释 1 数据2 RC.networkMultiLevel()中的参数解释2.1 测试*modes参数2.2 测试nodeCount参数2.3 测试edgeWeight参数2.4 测试stemmer参数2.5 测试edgeAttribute参数2.6 测试nodeAttribute参数2.7 测试_networkTypeString参数1 数据 RC.networkMul…

QT学习笔记(四)——在QLabel显示的影像上画图形,并和影像同步放大缩小

实现在QLabel显示的影像上画图形&#xff0c;并和影像同步放大缩小 关于影像在QLabel的显示&#xff0c;如何随鼠标滚轮实现放大缩小&#xff0c;可以参考我的上一篇博客 QT学习笔记&#xff08;三&#xff09;——vs2019Qt实现打开影像并以鼠标为中心用滚轮控制图片缩放 本篇…

[每周一更]-(第22期):什么是gRPC?

gRPC 是Google发起的一个开源远程过程调用 系统。 该系统基于HTTP/2 协议传输&#xff0c;使用Protocol Buffers 作为接口描述语言。 其他功能&#xff1a; 认证 双向流 流控制 超时 最常见的应用场景是&#xff1a; 微服务框架下&#xff0c;多种语言服务之间的高效交互。 …

app发布前要做的几件事

最近把一个小东西做完了&#xff0c;想上架让大家用一下。在上架之前关于app需要做的几件事在此备忘一下。 1、app签名。 我用的是android studio 21.2.1版&#xff0c;签名跟之前的版本可能不同&#xff08;我之前用的是3.5&#xff09;&#xff0c;但也大同小异。21.2.1版的…

蓝桥杯刷题四

1.激光炸弹 二维前缀和的模板题 这里注意一下边长是R 矩形是(R-1)*(R-1) 并且坐标最大是5000 所以5001的轰炸就能炸完整个图了 所以要对这个做优化 不然会各种错误 #include <bits/stdc.h> using namespace std; const int N5e310; int sum[N][N]; int main() {int n…

学生个人网页设计作品 HTML+CSS+JavaScript仿小米商城(8页) 学生个人网页模板 简单个人主页成品 个人网页制作 HTML学生个人网站作业设计

临近期末&#xff0c;大一新生的各种考试和专业结课作业纷至沓来。什么高数啊、线代啊、C语言、网页设计等&#xff0c;简直让人头大。你还在为网页设计老师的作业要求感到头大&#xff1f;网页作业无从下手&#xff1f;网页要求的总数量太多&#xff1f;没有合适的模板&#x…

《人月神话》(The Mythical Man-Month)4概念一致性:专制、民主和系统设计(System Design)...

主题&#xff1a;概念一致性 &#xff08;Conceptual Integrity&#xff09;4. 专制、民主和系统设计(System Design&#xff09;Chapter 4. Aristocracy, Democracy, and System Design大教堂是艺术史上无与伦比的成就。它的原则既不乏味也不混乱……真正达到了风格上的极致&a…

【蓝桥杯选拔赛真题30】python计算倒数和 青少年组蓝桥杯python 选拔赛STEMA比赛真题解析

目录 python计算倒数和 一、题目要求 1、编程实现 2、输入输出 3、评分标准

【深度学习】手写数字识别

一、机器学习问题的求解步骤 学习 使用训练数据进行权重参数的学习 推理使用学习到的参数对输入的数据进行分类 二、MNIST数据集 2.1 load_mnist(flattenTrue, normalizeFalse) flattentrue 读入的图像一维numpy数组的形式保存 2.2 函数学习 def fromarray(obj, modeNone…

第六章《类的高级特性》第2节:包的创建和使用

在一个Java工程中,往往会包含很多类。为了方便工程管理,程序员总是希望能够把这些类按照功能分开存放。为了达到这个目的,Java语言允许开发者把不同的类放入不同的包中。所谓“包”就是存放类的容器,在一个包中可以存放多个类。 6.2.1创建包 程序员使用IDEA可以很轻松的创…

开环控制(自动控制理论)

自动控制的基本原理方式 自动控制&#xff1a;是指没有人直接参与的情况下&#xff0c;利用控制装置&#xff08;称为控制器&#xff09;&#xff0c;使整个生产过程或工作机械&#xff08;称被控对象&#xff09;的某个工作状态或参数&#xff08;即被控量&#xff09;自动地按…

ubuntu 22.04 配置 Prometheus 和 Grafana 服务器监控

由于内存白菜价&#xff0c; 家里的虚拟机越来越多了。 1个是难以管理hostname 和 ip&#xff0c; 只靠/etc/hosts 去记住其实并不方便 试过宝塔linux面板&#xff0c; 可惜是单机的&#xff0c;不适用于管理集群linux服务器&#xff0c; 而且里面一股金钱气息。 至于他们另1…

李宏毅2017机器学习课程 回归

李宏毅2017机器学习课程 P3 回归 Regression 下文不区分w和ω&#xff08; 文章目录李宏毅2017机器学习课程 P3 回归 Regression回归定义举例:Pokemon精灵攻击力预测(Combat Power of a Pokemon)模型步骤Step1&#xff1a;模型假设-线性模型一元线性模型&#xff08;单个特征&…

搭建lnmp+nfs+调度器

→→→大虾好吃吗←←← 目录 搭建lnmp平台 nginx配置 mysql配置 php配置 验证 nfs调度器 安装服务 nfs配置 调度器配置 验证 →→→大虾好吃吗←←← 实验目标&#xff1a;搭建lnmp平台&#xff0c;安装wordpress论坛搭建nfs&#xff0c;客户端通过调度器访问nfs论坛。 实验拓…

pandas数据分析:十分钟快速入门重点函数速查

文章目录前言一、生成Series对象和DataFrame对象二、查看数据三、获取数据3.1 获取单列数据3.2 按照标签选择数据&#xff1a;loc函数3.2 按照位置选择数据&#xff1a;iloc函数3.3 布尔索引3.4 赋值四、缺失值五、运算六、merge&#xff1a;合并DataFrame6.1 concat函数6.2 me…

体系结构31_机群计算机

目前流行的高性能并行计算机系统结构通常可以分成五类&#xff1a;并行向量处理机&#xff08;PVP&#xff09;、对称多处理机&#xff08;SMP&#xff09;、大规模并行处理机&#xff08;MPP&#xff09;、分布共享存储&#xff08;DSM&#xff09;多处理机和机群&#xff08;…

现代密码学导论-13-归约证明

目录 3.3.2 Proofs by Reduction 最后来理一遍&#xff1a; 3.3.2 Proofs by Reduction 如果我们希望证明一个给定的构造&#xff08;例如&#xff0c;加密方案&#xff09;在计算上是安全的&#xff0c;那么——除非该方案在信息理论上是安全的——我们必须依赖于未经证明的…

影像匹配基本算法(摄影测量)

影像匹配实质上是在两幅(或多幅)影像之间识别同名点&#xff0c;它是计算机视觉及数字摄影测量的核心问题。由于早期的研究一般使用相关技术解决影像匹配问题&#xff0c;所以影像匹配常常被称为影像相关。 根据影像匹配过程中匹配基元选择的不同&#xff0c;影像匹配方法可分…

iptables应用大全

iptables四表五链&#xff1a; 1、“四表”是指 iptables 的功能 ——filter 表&#xff08;过滤规则表&#xff09;&#xff1a;控制数据包是否允许进出及转发 ——nat 表&#xff08;地址转换规则表&#xff09;&#xff1a;控制数据包中地址转换 ——mangle&#xff08;修改…

【C++笔试强训】第三十天

&#x1f387;C笔试强训 博客主页&#xff1a;一起去看日落吗分享博主的C刷题日常&#xff0c;大家一起学习博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a;夜色难免微凉&#xff0c;前方必有曙光 &#x1f31e;。 &#x1f4a6;&a…