SpringCloud微服务网关进行XSS攻击过滤-Zuul网关篇

XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。
通俗来说,在新增表单里,表单内容里插入一段html或者js代码,在列表渲染时就可能执行这串js代码。这就是xss攻击。

在微服务中,我们可以很方便的去网关中处理xss攻击,即自定义过滤器,在网关进行路径分发前进行输入数据的处理

直接编写自定义过滤器,继承ZuulFilter过滤器

package com.bp.getway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.netflix.zuul.http.ServletInputStreamWrapper;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.*;

/**
 * @author ZhuWeiHao
 * @date 2022/1/18
 * XSS过滤
 */
@Component
@Log4j2
public class XSSFilter extends ZuulFilter {


    //类型 pre,在之前处理
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    //Spring的排序,越小越早加载
    @Override
    public int filterOrder() {
        return -99;
    }

    //是否执行逻辑处理
    @Override
    public boolean shouldFilter() {
        return true;
    }

    //过滤处理
    @Override
    public Object run() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        String contentType = request.getContentType();
        if (StringUtils.isNotBlank(contentType)) {
            //表单和url提交方式
            if (StringUtils.equals(contentType, "application/x-www-form-urlencoded")
                    || StringUtils.equals(contentType, "application/x-www-form-urlencoded;charset=UTF-8")) {

                //对象引用不允许直接对原对象进行修改
                Map<String, String[]> parameterMap = new HashMap(request.getParameterMap());
                if (parameterMap == null || parameterMap.isEmpty()) {
                    return null;
                }
                Map<String, List<String>> requestQueryParams = requestContext.getRequestQueryParams();
                if (requestQueryParams == null) {
                    requestQueryParams = new HashMap<>(parameterMap.size() * 2);
                }
                Iterator<Map.Entry<String, String[]>> iterator = parameterMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, String[]> next = iterator.next();
                    List<String> list = Arrays.asList(next.getValue());
                    for (int i = 0; i < list.size(); i++) {
                        list.set(i, StringEscapeUtils.escapeJavaScript(StringEscapeUtils.escapeHtml(list.get(i))));
                    }
                    String key = next.getKey();
                    requestQueryParams.put(key, list);
                }
                requestContext.setRequestQueryParams(requestQueryParams);
                //json提交方式
            } else if (StringUtils.equals(contentType, "application/json")
                    || StringUtils.equals(contentType, "application/json;charset=UTF-8")) {

                try {
                    InputStream in = requestContext.getRequest().getInputStream();
                    String body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
                    JSONObject json = JSON.parseObject(body);
                    Map<String, Object> map = json;
                    Map<String, Object> mapJson = new HashMap<>();
                    for (Map.Entry<String, Object> entry : map.entrySet()) {
                        mapJson.put(entry.getKey(), cleanXSS(entry.getValue().toString()));
                    }
                    String newBody = JSON.toJSONString(mapJson);
                    byte[] reqBodyBytes = newBody.getBytes();

                    //注意此处的HttpServletRequestWrapper 是java.servlet包下的
                    //重写输入流
                    requestContext.setRequest(new HttpServletRequestWrapper(request) {
                        @Override
                        public ServletInputStream getInputStream() {
                            return new ServletInputStreamWrapper(reqBodyBytes);
                        }

                        //注意此次两个获取内容长度方法一定要重写,不然容易造成读取长度不一致的问题
                        @Override
                        public int getContentLength() {
                            return reqBodyBytes.length;
                        }

                        @Override
                        public long getContentLengthLong() {
                            return reqBodyBytes.length;
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                    log.error("xss过滤器读取参数异常");
                }

            }


        }

        return null;
    }

    private String cleanXSS(String value) {
        if (StringUtils.isBlank(value)) {
            return value;
        }
        value = StringEscapeUtils.escapeHtml(value);
        value = StringEscapeUtils.escapeJavaScript(value);
        value = value.replaceAll("\\\\", "");
        return value;
    }

}

运行项目,进行输入测试,xss代码被转义了,效果达到

Spring Cloud 项目中,通常会使用 Spring Cloud Gateway 作为网关,可以通过自定义过滤器来实现 XSS 过滤。 以下是一个使用 Spring Cloud Gateway 实现 XSS 过滤的示例: 1. 引入依赖 在 pom.xml 文件中添加以下依赖: ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.googlecode.htmlcompressor</groupId> <artifactId>htmlcompressor</artifactId> <version>1.5.3</version> </dependency> ``` 其中,`htmlcompressor` 是一个开源的 HTML 压缩工具,可以用来过滤 XSS 攻击。 2. 实现过滤器 创建一个名为`XssFilter`的过滤器,实现`GatewayFilter`接口,重写`filter`方法,在该方法中对请求和响应进行过滤。 ```java public class XssFilter implements GatewayFilter { private final HtmlCompressor compressor; public XssFilter() { compressor = new HtmlCompressor(); compressor.setPreserveLineBreaks(false); compressor.setRemoveComments(true); compressor.setRemoveIntertagSpaces(true); } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); HttpHeaders headers = response.getHeaders(); MediaType mediaType = headers.getContentType(); if (mediaType != null && mediaType.isCompatibleWith(MediaType.TEXT_HTML)) { return chain.filter(exchange.mutate() .request(wrapRequest(request)) .response(wrapResponse(response)) .build()); } return chain.filter(exchange); } private ServerHttpRequest wrapRequest(ServerHttpRequest request) { return new ServerHttpRequestDecorator(request) { @Override public Flux<DataBuffer> getBody() { return super.getBody().map(buffer -> { byte[] bytes = new byte[buffer.readableByteCount()]; buffer.read(bytes); DataBufferUtils.release(buffer); String body = new String(bytes, StandardCharsets.UTF_8); return bufferFactory().wrap(compressor.compress(body).getBytes(StandardCharsets.UTF_8)); }); } }; } private ServerHttpResponse wrapResponse(ServerHttpResponse response) { return new ServerHttpResponseDecorator(response) { @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { if (body instanceof Flux) { Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body; return super.writeWith(fluxBody.map(buffer -> { byte[] bytes = new byte[buffer.readableByteCount()]; buffer.read(bytes); DataBufferUtils.release(buffer); String bodyStr = new String(bytes, StandardCharsets.UTF_8); return bufferFactory().wrap(compressor.compress(bodyStr).getBytes(StandardCharsets.UTF_8)); })); } return super.writeWith(body); } }; } } ``` 在上述代码中,通过`HtmlCompressor`压缩 HTML 代码,来过滤恶意脚本。`wrapRequest`方法和`wrapResponse`方法分别对请求和响应进行过滤。需要注意的是,在响应中的`writeWith`方法中,需要判断响应流是否为`Flux`类型,如果是,则需要对每个`DataBuffer`进行过滤。 3. 配置过滤器 在 Spring Cloud Gateway 中配置过滤器需要在配置类中实现`GatewayFilterFactory`接口,实现`apply`方法,返回一个自定义的过滤器实例。 ```java @Configuration public class GatewayConfig { @Bean public XssGatewayFilterFactory xssGatewayFilterFactory() { return new XssGatewayFilterFactory(); } public static class XssGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> { @Override public GatewayFilter apply(Object config) { return new XssFilter(); } @Override public List<String> shortcutFieldOrder() { return Collections.emptyList(); } } } ``` 在上述代码中,定义了一个名为`XssGatewayFilterFactory`的过滤器工厂,返回一个`XssFilter`过滤器实例。在`shortcutFieldOrder`方法中返回一个空列表,表示不需要任何配置参数。 4. 配置路由 在配置路由时,将`XssGatewayFilterFactory`加入到过滤器链中。 ```yaml spring: cloud: gateway: routes: - id: example uri: http://example.com filters: - xss ``` 在上述配置中,将`XssGatewayFilterFactory`定义的过滤器加入到路由的过滤器链中,即可实现 XSS 过滤。 需要注意的是,在实际应用中,还需要根据具体情况进行适当调整和优化。例如,可以根据请求和响应头中的`Content-Type`判断是否需要进行过滤,以提高性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值