当前位置: 代码网 > it编程>编程语言>Java > SpringBoot如何使用过滤器进行XSS防御

SpringBoot如何使用过滤器进行XSS防御

2024年11月16日 Java 我要评论
在spring boot中,我们可以使用注解的方式来进行xss防御。注解是一种轻量级的防御手段,它可以在方法或字段级别对输入进行校验,从而防止xss攻击。而想对全局的请求都进行xss防御可以使用ser

在spring boot中,我们可以使用注解的方式来进行xss防御。注解是一种轻量级的防御手段,它可以在方法或字段级别对输入进行校验,从而防止xss攻击。

而想对全局的请求都进行xss防御可以使用servlet中的过滤器或者spring mvc中的拦截器,这里使用servlet中的过滤器进行演示。

引入相关依赖

maven依赖:

<!--jsr-303/jsr-380用于验证的注解 -->
<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-validation</artifactid>
    <version>2.6.7</version>
</dependency>

如果是使用grade,引入依赖:

implementation 'org.springframework.boot:spring-boot-starter-validation:2.6.7'

修改配置文件

xss:
  enabled: true
  excludeurllist:
    - /xss/local/test

定义配置文件对应的属性类

package com.morris.spring.boot.module.xss;

import lombok.data;

import java.util.list;

@data
public class xssfilterproperties {
    /**
     * 是否启用xss过滤。
     */
    private boolean enabled = true;

    /**
     * 需要排除的url模式,这些url不会进行xss过滤。
     */
    private list<string> excludeurllist;
}

注入xss配置类

package com.morris.spring.boot.module.xss;

import lombok.data;
import org.springframework.boot.autoconfigure.http.httpmessageconverters;
import org.springframework.boot.context.properties.configurationproperties;
import org.springframework.boot.web.servlet.filterregistrationbean;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.http.converter.httpmessageconverter;

import javax.servlet.dispatchertype;

@data
@configuration
public class xssfilterconfig {

    @configurationproperties(prefix = "xss")
    @bean
    public xssfilterproperties xssfilterproperties() {
        return new xssfilterproperties();
    }

    /**
     * 注册xss过滤器。
     *
     * @return filterregistrationbean 用于注册过滤器的bean。
     */
    @bean
    public filterregistrationbean<xssfilter> xssfilterregistration(xssfilterproperties xssfilterproperties) {
        filterregistrationbean<xssfilter> registrationbean = new filterregistrationbean<>();
        // 设置过滤器的分发类型为请求类型
        registrationbean.setdispatchertypes(dispatchertype.request);
        // 创建xssfilter的实例
        registrationbean.setfilter(new xssfilter(xssfilterproperties));
        // 添加过滤器需要拦截的url模式,这里拦截所有请求
        registrationbean.addurlpatterns("/*");
        // 设置过滤器的名称
        registrationbean.setname("xssfilter");
        // 设置过滤器的执行顺序,数值越小,优先级越高
        registrationbean.setorder(9999);
        return registrationbean;
    }

    @bean
    public httpmessageconverters xsshttpmessageconverters() {
        xssmappingjackson2httpmessageconverter xssmappingjackson2httpmessageconverter = new xssmappingjackson2httpmessageconverter();
        httpmessageconverter converter = xssmappingjackson2httpmessageconverter;
        return new httpmessageconverters(converter);
    }

}

xssfilter过滤器

xssfilter过滤器会将所有需要进行防御的请求包装为xsswrapper。

package com.morris.spring.boot.module.xss;

import lombok.extern.slf4j.slf4j;
import org.springframework.util.collectionutils;

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;

@slf4j
public class xssfilter implements filter {

    private final xssfilterproperties xssfilterproperties;

    public xssfilter(xssfilterproperties xssfilterproperties) {
        this.xssfilterproperties = xssfilterproperties;
    }

    /**
     * 执行过滤逻辑,如果当前请求不在排除列表中,则通过xss过滤器包装请求。
     *
     * @param request  http请求对象。
     * @param response http响应对象。
     * @param chain    过滤器链对象,用于继续或中断请求处理。
     * @throws ioexception      如果处理过程中出现i/o错误。
     * @throws servletexception 如果处理过程中出现servlet相关错误。
     */
    @override
    public void dofilter(servletrequest request, servletresponse response, filterchain chain) throws ioexception, servletexception {
        httpservletrequest req = (httpservletrequest) request;
        httpservletresponse resp = (httpservletresponse) response;
        //如果该访问接口在排除列表里面则不拦截
        if (isexcludeurl(req.getservletpath())) {
            chain.dofilter(request, response);
            return;
        }

        log.info("uri:{}", req.getrequesturi());
        // xss 过滤
        chain.dofilter(new xsswrapper(req), resp);
    }

    /**
     * 判断当前请求的url是否应该被排除在xss过滤之外。
     *
     * @param urlpath 请求的url路径。
     * @return 如果请求应该被排除,则返回true;否则返回false。
     */
    private boolean isexcludeurl(string urlpath) {
        if (!xssfilterproperties.isenabled()) {
            //如果xss开关关闭了,则所有url都不拦截
            return true;
        }

        if(collectionutils.isempty(xssfilterproperties.getexcludeurllist())) {
            return false;
        }

        for (string pattern : xssfilterproperties.getexcludeurllist()) {
            pattern p = pattern.compile("^" + pattern);
            matcher m = p.matcher(urlpath);
            if (m.find()) {
                return true;
            }
        }
        return false;
    }
}

xsswrapper过滤get请求和请求头

xsswrapper会过滤get请求和请求头中的非法字符。

package com.morris.spring.boot.module.xss;

import lombok.extern.slf4j.slf4j;
import org.apache.commons.lang3.stringutils;

import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletrequestwrapper;

@slf4j
public class xsswrapper extends httpservletrequestwrapper {

    public xsswrapper(httpservletrequest request) {
        super(request);
    }

    /**
     * 对数组参数进行特殊字符过滤
     */
    @override
    public string[] getparametervalues(string name) {
        string[] values = super.getparametervalues(name);
        if (values == null) {
            return null;
        }
        int count = values.length;
        string[] encodedvalues = new string[count];
        for (int i = 0; i < count; i++) {
            encodedvalues[i] = xssutil.clean(values[i]);
        }
        return encodedvalues;
    }

    /**
     * 对参数中特殊字符进行过滤
     */
    @override
    public string getparameter(string name) {
        string value = super.getparameter(name);
        if (stringutils.isblank(value)) {
            return value;
        }
        return xssutil.clean(value);
    }

    /**
     * 获取attribute,特殊字符过滤
     */
    @override
    public object getattribute(string name) {
        object value = super.getattribute(name);
        if (value instanceof string && stringutils.isnotblank((string) value)) {
            return xssutil.clean((string) value);
        }
        return value;
    }

    /**
     * 对请求头部进行特殊字符过滤
     */
    @override
    public string getheader(string name) {
        string value = super.getheader(name);
        if (stringutils.isblank(value)) {
            return value;
        }
        return xssutil.clean(value);
    }
}

messageconverter过滤post请求

package com.morris.spring.boot.module.xss;

import com.fasterxml.jackson.databind.javatype;
import org.springframework.http.httpinputmessage;
import org.springframework.http.httpoutputmessage;
import org.springframework.http.converter.httpmessagenotreadableexception;
import org.springframework.http.converter.httpmessagenotwritableexception;
import org.springframework.http.converter.json.mappingjackson2httpmessageconverter;

import java.io.ioexception;
import java.lang.reflect.type;

/**
 * 在读取和写入json数据时特殊字符避免xss攻击的消息解析器
 *
 */
public class xssmappingjackson2httpmessageconverter extends mappingjackson2httpmessageconverter {

    /**
     * 从http输入消息中读取对象,同时应用xss防护。
     * 
     * @param type        类型令牌,表示要读取的对象类型。
     * @param contextclass    上下文类,提供类型解析的上下文信息。
     * @param inputmessage http输入消息,包含要读取的json数据。
     * @return 从输入消息中解析出的对象,经过xss防护处理。
     * @throws ioexception 如果发生i/o错误。
     * @throws httpmessagenotreadableexception 如果消息无法读取。
     */
    @override
    public object read(type type, class contextclass,
                       httpinputmessage inputmessage) throws ioexception,
            httpmessagenotreadableexception {
        javatype javatype = getjavatype(type, contextclass);
        object obj = readjavatype(javatype, inputmessage);
        //得到请求json
        string json = super.getobjectmapper().writevalueasstring(obj);
        //过滤特殊字符
        string result = xssutil.clean(json);
        object resultobj = super.getobjectmapper().readvalue(result, javatype);
        return resultobj;
    }

    /**
     * 从http输入消息中读取指定java类型的对象,内部使用。
     * 
     * @param javatype    要读取的对象的java类型。
     * @param inputmessage http输入消息,包含要读取的json数据。
     * @return 从输入消息中解析出的对象。
     * @throws ioexception 如果发生i/o错误。
     * @throws httpmessagenotreadableexception 如果消息无法读取。
     */
    private object readjavatype(javatype javatype, httpinputmessage inputmessage) {
        try {
            return super.getobjectmapper().readvalue(inputmessage.getbody(), javatype);
        } catch (ioexception ex) {
            throw new httpmessagenotreadableexception("could not read json: " + ex.getmessage(), ex);
        }
    }

    /**
     * 将对象写入http输出消息,同时应用xss防护。
     * 
     * @param object 要写入的对象。
     * @param outputmessage http输出消息,对象将被序列化为json并写入此消息。
     * @throws ioexception 如果发生i/o错误。
     * @throws httpmessagenotwritableexception 如果消息无法写入。
     */
    @override
    protected void writeinternal(object object, httpoutputmessage outputmessage)
            throws ioexception, httpmessagenotwritableexception {
        //得到要输出的json
        string json = super.getobjectmapper().writevalueasstring(object);
        //过滤特殊字符
        string result = xssutil.clean(json);
        // 输出
        outputmessage.getbody().write(result.getbytes());
    }
}

xss过滤工具类

package com.morris.spring.boot.module.xss;

import org.jsoup.jsoup;
import org.jsoup.nodes.document;
import org.jsoup.safety.whitelist;

/**
 * xss过滤工具类,使用jsoup库对输入的字符串进行xss攻击防护
 */
public class xssutil {

    /**
     * 使用jsoup自带的relaxed白名单
     */
    private static final whitelist white_list = whitelist.relaxed();
    /**
     * 定义输出设置,关闭prettyprint(prettyprint=false),目的是避免在清理过程中对代码进行格式化
     * 从而保持输入和输出内容的一致性。
     */
    private static final document.outputsettings output_settings = new document.outputsettings().prettyprint(false);

    /*
      初始化白名单策略,允许所有标签拥有style属性。
      这是因为在富文本编辑中,样式通常通过style属性来定义,需要确保这些样式能够被保留。
     */
    static {
        // 富文本编辑时一些样式是使用 style 来进行实现的
        // 比如红色字体 style="color:red;"
        // 所以需要给所有标签添加 style 属性
        white_list.addattributes(":all", "style");
    }

    /**
     * 清理输入的字符串,移除潜在的xss攻击代码。
     *
     * @param content 待清理的字符串,通常是用户输入的html内容。
     * @return 清理后的字符串,保证不包含xss攻击代码。
     */
    public static string clean(string content) {
        // 使用定义好的白名单策略和输出设置清理输入的字符串
        return jsoup.clean(content, "", white_list, output_settings);
    }
}

get请求测试

package com.morris.spring.boot.module.xss;

import org.springframework.validation.annotation.validated;
import org.springframework.web.bind.annotation.getmapping;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.restcontroller;


/**
 * xss2防御get请求
 */
@restcontroller
@requestmapping("/xss/global")
@validated
public class xssglobalgetcontroller {

    /**
     * 使用注解拦截get请求中的xss,在方法参数前面加上@xss,注意类上面要加上@validated注解
     *
     * @param useraccount 请求参数
     * @return 请求参数
     */
    @getmapping("/test")
    public string test(string useraccount) {
        return useraccount;
    }

}

发送get请求:http://localhost:8888/xss/global/test?useraccount=demodata

返回结果:demodata

post请求测试

package com.morris.spring.boot.module.xss;

import org.springframework.web.bind.annotation.postmapping;
import org.springframework.web.bind.annotation.requestbody;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.restcontroller;

/**
 * xss全局防御post请求
 */
@restcontroller
@requestmapping("/xss/global")
public class xssglobalpostcontroller {

    /**
     * 使用注解拦截post请求中的xss,在实体类需要拦截xss的属性上面加上@xss或者@validated注解
     *
     * @param usergloballoginpojo 实体类
     * @return 实体类
     */
    @postmapping("/test")
    public usergloballoginpojo test(@requestbody usergloballoginpojo usergloballoginpojo) {
        return usergloballoginpojo;
    }

}

发送post请求:http://localhost:8888/xss/global/test

请求体:

{
    "useraccount": "<iframe οnlοad='alert(0)'>demodata</iframe>"
}

返回结果:

{
    "useraccount": "demodata"
}

到此这篇关于springboot如何使用过滤器进行xss防御的文章就介绍到这了,更多相关springboot xss防御内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com