Springboot抵御即跨站脚本(XSS)攻击

一、XSS攻击的危害  

       XSS 攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网
页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是 JavaScript ,但实
际上也可以包括 Java VBScript ActiveX Flash 或者甚至是普通的 HTML 。攻击成功后,攻击
者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和 cookie 等各种
内容。
        例如用户在发帖或者注册的时候,在文本框中输入 <script>alert('xss')</script> ,这段代码
如果不经过转义处理,而直接保存到数据库。将来视图层渲染 HTML 的时候,把这段代码输出到
页面上,那么 <script> 标签的内容就会被执行。
        通常情况下,我们登陆到某个网站。如果网站使用 HttpSession 保存登陆凭证,那么
SessionId 会以 Cookie 的形式保存在浏览器上。如果黑客在这个网页发帖的时候,填写的
JavaScript 代码是用来获取 Cookie 内容的,并且把 Cookie 内容通过 Ajax 发送给黑客自己的电
脑。于是只要有人在这个网站上浏览黑客发的帖子,那么视图层渲染 HTML 页面,就会执行注入
XSS 脚本,于是你的 Cookie 信息就泄露了。黑客在自己的电脑上构建出 Cookie ,就可以冒
充已经登陆的用户。
   即便很多网站使用了 JWT ,登陆凭证( Token 令牌 )是存储在浏览器上面的。所以用 XSS 脚本可
以轻松的从 Storage 中提取出 Token ,黑客依然可以轻松的冒充已经登陆的用户。
        
    所以避免 XSS 攻击最有效的办法就是对用户输入的数据进行转义,然后存储到数据库里面。等到
视图层渲染 HTML 页面的时候。转义后的文字是不会被当做 JavaScript 执行的,这就可以抵御 XSS
攻击。

二、导入依赖库

        因为 Hutool 工具包带有XSS转义的工具类,所以我们要导入 Hutool ,然后利用 Servlet 规范

提供的请求包装类,定义数据转义功能。
 
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.4.0</version>
</dependency>

三、定义请求包装类

      我们平时写 Web 项目遇到的 HttpServletRequest ,它其实是个接口。如果我们想要重新定义请
求类,扩展这个接口是最不应该的。因为 HttpServletRequest 接口中抽象方法太多了,我们逐
一实现起来太耗费时间。所以我们应该挑选一个简单一点的自定义请求类的方式。那就是继承
HttpServletRequestWrapper 父类。
        
      JavaEE 只是一个标准,具体的实现由各家应用服务器厂商来完成。比如说 Tomcat 在实现
Servlet 规范的时候,就自定义了 HttpServletRequest 接口的实现类。同时 JavaEE 规范还定义
HttpServletRequestWrapper ,这个类是请求类的包装类,用上了装饰器模式。不得不说这里
用到的设计模式真的非常棒,无论各家应用服务器厂商怎么去实现 HttpServletRequest 接口,
用户想要自定义请求,只需要继承 HttpServletRequestWrapper ,对应覆盖某个方法即可,然后
把请求传入请求包装类,装饰器模式就会替代请求对象中对应的某个方法。用户的代码和服务器
厂商的代码完全解耦,我们不用关心 HttpServletRequest 接口是怎么实现的,借助于包装类我
们可以随意修改请求中的方法。同学们,如此优雅的代码设计,有时间你真该认真学习设计模
式。
package com.example.emos.wx.config.xss;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import cn.hutool.json.JSONUtil;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.Map;

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

    @Override
    public String getParameter(String name) {
        String value= super.getParameter(name);
        if(!StrUtil.hasEmpty(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(!StrUtil.hasEmpty(value)){
                    value=HtmlUtil.filter(value);
                }
                values[i]=value;
            }
        }
        return values;
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> parameters = super.getParameterMap();
        LinkedHashMap<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 (!StrUtil.hasEmpty(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 (!StrUtil.hasEmpty(value)) {
            value = HtmlUtil.filter(value);
        }
        return value;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        InputStream in= super.getInputStream();
        InputStreamReader reader=new InputStreamReader(in, Charset.forName("UTF-8"));
        BufferedReader buffer=new BufferedReader(reader);
        StringBuffer body=new StringBuffer();
        String line=buffer.readLine();
        while(line!=null){
            body.append(line);
            line=buffer.readLine();
        }
        buffer.close();
        reader.close();
        in.close();
        Map<String,Object> map=JSONUtil.parseObj(body.toString());
        Map<String,Object> result=new LinkedHashMap<>();
        for(String key:map.keySet()){
            Object val=map.get(key);
            if(val instanceof String){
                if(!StrUtil.hasEmpty(val.toString())){
                    result.put(key,HtmlUtil.filter(val.toString()));
                }
            }
            else {
                result.put(key,val);
            }
        }
        String json=JSONUtil.toJsonStr(result);
        ByteArrayInputStream bain=new ByteArrayInputStream(json.getBytes());
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bain.read();
            }

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

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

            @Override
            public void setReadListener(ReadListener readListener) {

            }
        };
    }
}

四、创建过滤器,把所有请求对象传入包装类

        为了让刚刚定义的包装类生效,我们还要在 com.example.emos.wx.config.xss 中创建

XssFilter 过滤器。过滤器拦截所有请求,然后把请求传入包装类,这样包装类就能覆盖所有请
求的参数方法,用户从请求中获得数据,全都经过转义。
package com.example.emos.wx.config.xss;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class XssFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

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

    @Override
    public void destroy() {

    }
}

五、给主类添加注解

        SpringBoot主类添加 @ServletComponentScan 注解。

猜你喜欢

转载自blog.csdn.net/qq_67801847/article/details/133181410
今日推荐