国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Spring Cloud Gateway網關XSS過濾方式

Spring Cloud Gateway網關XSS過濾方式

2022-02-17 14:50千年的心 Java教程

這篇文章主要介紹了Spring Cloud Gateway網關XSS過濾方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

XSS是一種經常出現在web應用中的計算機安全漏洞,具體信息請自行Google。本文只分享在Spring Cloud Gateway中執行通用的XSS防范。首次作文,全是代碼,若有遺漏不明之處,請各位看官原諒指點。

使用版本

  • Spring Cloud版本為 Greenwich.SR4
  • Spring Boot版本為 2.1.11.RELEASE

1.創建一個Filter

特別注意的是在處理完成之后需要重新構造請求,否則后續業務無法獲得參數。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import io.netty.buffer.ByteBufAllocator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.DigestUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import javax.validation.constraints.NotEmpty;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;
/**
 * XSS過濾
 *
 * @author lieber
 */
@Component
@Slf4j
@ConfigurationProperties("config.xss")
@Data
public class XssFilter implements GlobalFilter, Ordered {
    private List<XssWhiteUrl> whiteUrls;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        URI uri = request.getURI();
        String method = request.getMethodValue();
        // 判斷是否在白名單中
        if (this.white(uri.getPath(), method)) {
            return chain.filter(exchange);
        }
        // 只攔截POST和PUT請求
        if ((HttpMethod.POST.name().equals(method) || HttpMethod.PUT.name().equals(method))) {
            return DataBufferUtils.join(request.getBody())
                    .flatMap(dataBuffer -> {
                        // 取出body中的參數
                        byte[] oldBytes = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(oldBytes);
                        String bodyString = new String(oldBytes, StandardCharsets.UTF_8);
                        log.debug("原請求參數為:{}", bodyString);
                        // 執行XSS清理
                        bodyString = XssUtil.INSTANCE.cleanXss(bodyString);
                        log.debug("修改后參數為:{}", bodyString);
                        ServerHttpRequest newRequest = request.mutate().uri(uri).build();
                        // 重新構造body
                        byte[] newBytes = bodyString.getBytes(StandardCharsets.UTF_8);
                        DataBuffer bodyDataBuffer = toDataBuffer(newBytes);
                        Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
                        // 重新構造header
                        HttpHeaders headers = new HttpHeaders();
                        headers.putAll(request.getHeaders());
                        // 由于修改了傳遞參數,需要重新設置CONTENT_LENGTH,長度是字節長度,不是字符串長度
                        int length = newBytes.length;
                        headers.remove(HttpHeaders.CONTENT_LENGTH);
                        headers.setContentLength(length);
                        headers.set(HttpHeaders.CONTENT_TYPE, "application/json;charset=utf8");
                        // 重寫ServerHttpRequestDecorator,修改了body和header,重寫getBody和getHeaders方法
                        newRequest = new ServerHttpRequestDecorator(newRequest) {
                            @Override
                            public Flux<DataBuffer> getBody() {
                                return bodyFlux;
                            }
                            @Override
                            public HttpHeaders getHeaders() {
                                return headers;
                            }
                        };
                        return chain.filter(exchange.mutate().request(newRequest).build());
                    });
        } else {
            return chain.filter(exchange);
        }
    }
    /**
     * 是否是白名單
     *
     * @param url    路由
     * @param method 請求方式
     * @return true/false
     */
    private boolean white(String url, String method) {
        return whiteUrls != null && whiteUrls.contains(XssWhiteUrl.builder().url(url).method(method).build());
    }
    /**
     * 字節數組轉DataBuffer
     *
     * @param bytes 字節數組
     * @return DataBuffer
     */
    private DataBuffer toDataBuffer(byte[] bytes) {
        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
        DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
        buffer.write(bytes);
        return buffer;
    }
    public static final int ORDER = 10;
    @Override
    public int getOrder() {
        return ORDER;
    }
    @Data
    @Validated
    @AllArgsConstructor
    @NoArgsConstructor
    private static class XssWhiteUrl {
        @NotEmpty
        private String url;
        @NotEmpty
        private String method;
    }
}

2. 處理XSS字符串

這里大范圍采用Jsoup處理,然后根據自己的業務做了一部分定制。較為特殊的是,我們將字符串中含有'</'標示為這段文本是富文本。在清除xss攻擊字符串方法時優化空間較大。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import com.alibaba.fastjson.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.safety.Whitelist;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
 * xss攔截工具類
 *
 * @author lieber
 */
public enum XssUtil {
    /**
     * 實例
     */
    INSTANCE;
    private final static String RICH_TEXT = "</";
    /**
     * 自定義白名單
     */
    private final static Whitelist CUSTOM_WHITELIST = Whitelist.relaxed()
            .addAttributes("video", "width", "height", "controls", "alt", "src")
            .addAttributes(":all", "style", "class");
    /**
     * jsoup不格式化代碼
     */
    private final static Document.OutputSettings OUTPUT_SETTINGS = new Document.OutputSettings().prettyPrint(false);
    /**
     * 清除json對象中的xss攻擊字符
     *
     * @param val json對象字符串
     * @return 清除后的json對象字符串
     */
    private String cleanObj(String val) {
        JSONObject jsonObject = JSONObject.parseObject(val);
        for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
            if (entry.getValue() != null && entry.getValue() instanceof String) {
                String str = (String) entry.getValue();
                str = this.cleanXss(str);
                entry.setValue(str);
            }
        }
        return jsonObject.toJSONString();
    }
    /**
     * 清除json數組中的xss攻擊字符
     *
     * @param val json數組字符串
     * @return 清除后的json數組字符串
     */
    private String cleanArr(String val) {
        List<String> list = JSONObject.parseArray(val, String.class);
        List<String> result = new ArrayList<>(list.size());
        for (String str : list) {
            str = this.cleanXss(str);
            result.add(str);
        }
        return JSONObject.toJSONString(result);
    }
    /**
     * 清除xss攻擊字符串,此處優化空間較大
     *
     * @param str 字符串
     * @return 清除后無害的字符串
     */
    public String cleanXss(String str) {
        if (JsonUtil.INSTANCE.isJsonObj(str)) {
            str = this.cleanObj(str);
        } else if (JsonUtil.INSTANCE.isJsonArr(str)) {
            str = this.cleanArr(str);
        } else {
            boolean richText = this.richText(str);
            if (!richText) {
                str = str.trim();
                str = str.replaceAll(" +", " ");
            }
            String afterClean = Jsoup.clean(str, "", CUSTOM_WHITELIST, OUTPUT_SETTINGS);
            if (paramError(richText, afterClean, str)) {
                throw new BizRunTimeException(ApiCode.PARAM_ERROR, "參數包含特殊字符");
            }
            str = richText ? afterClean : this.backSpecialStr(afterClean);
        }
        return str;
    }
    /**
     * 判斷是否是富文本
     *
     * @param str 待判斷字符串
     * @return true/false
     */
    private boolean richText(String str) {
        return str.contains(RICH_TEXT);
    }
    /**
     * 判斷是否參數錯誤
     *
     * @param richText   是否富文本
     * @param afterClean 清理后字符
     * @param str        原字符串
     * @return true/false
     */
    private boolean paramError(boolean richText, String afterClean, String str) {
        // 如果包含富文本字符,那么不是參數錯誤
        if (richText) {
            return false;
        }
        // 如果清理后的字符和清理前的字符匹配,那么不是參數錯誤
        if (Objects.equals(str, afterClean)) {
            return false;
        }
        // 如果僅僅包含可以通過的特殊字符,那么不是參數錯誤
        if (Objects.equals(str, this.backSpecialStr(afterClean))) {
            return false;
        }
        // 如果還有......
        return true;
    }
    /**
     * 轉義回特殊字符
     *
     * @param str 已經通過轉義字符
     * @return 轉義后特殊字符
     */
    private String backSpecialStr(String str) {
        return str.replaceAll("&amp;", "&");
    }
}

3.其它使用到的工具

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import com.alibaba.fastjson.JSONObject;
import org.springframework.util.StringUtils;
/**
 * JSON處理工具類
 *
 * @author lieber
 */
public enum JsonUtil {
    /**
     * 實例
     */
    INSTANCE;
    /**
     * json對象字符串開始標記
     */
    private final static String JSON_OBJECT_START = "{";
    /**
     * json對象字符串結束標記
     */
    private final static String JSON_OBJECT_END = "}";
    /**
     * json數組字符串開始標記
     */
    private final static String JSON_ARRAY_START = "[";
    /**
     * json數組字符串結束標記
     */
    private final static String JSON_ARRAY_END = "]";
    /**
     * 判斷字符串是否json對象字符串
     *
     * @param val 字符串
     * @return true/false
     */
    public boolean isJsonObj(String val) {
        if (StringUtils.isEmpty(val)) {
            return false;
        }
        val = val.trim();
        if (val.startsWith(JSON_OBJECT_START) && val.endsWith(JSON_OBJECT_END)) {
            try {
                JSONObject.parseObject(val);
                return true;
            } catch (Exception e) {
                return false;
            }
        }
        return false;
    }
    /**
     * 判斷字符串是否json數組字符串
     *
     * @param val 字符串
     * @return true/false
     */
    public boolean isJsonArr(String val) {
        if (StringUtils.isEmpty(val)) {
            return false;
        }
        val = val.trim();
        if (StringUtils.isEmpty(val)) {
            return false;
        }
        val = val.trim();
        if (val.startsWith(JSON_ARRAY_START) && val.endsWith(JSON_ARRAY_END)) {
            try {
                JSONObject.parseArray(val);
                return true;
            } catch (Exception e) {
                return false;
            }
        }
        return false;
    }
    /**
     * 判斷對象是否是json對象
     *
     * @param obj 待判斷對象
     * @return true/false
     */
    public boolean isJsonObj(Object obj) {
        String str = JSONObject.toJSONString(obj);
        return this.isJsonObj(str);
    }
    /**
     * 判斷字符串是否json字符串
     *
     * @param str 字符串
     * @return true/false
     */
    public boolean isJson(String str) {
        if (StringUtils.isEmpty(str)) {
            return false;
        }
        return this.isJsonObj(str) || this.isJsonArr(str);
    }
}

大功告成。

----------------手動分隔----------------

修改

感謝@chang_p_x的指正,在第一步創建Filter時有問題,原因是使用了新舊代碼的問題,現已經將元代碼放在正文,新代碼如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        URI uri = request.getURI();
        String method = request.getMethodValue();
        if (this.white(uri.getPath(), method)) {
            return chain.filter(exchange);
        }
        if ((HttpMethod.POST.name().equals(method) || HttpMethod.PUT.name().equals(method))) {
            return DataBufferUtils.join(request.getBody()).flatMap(d -> Mono.just(Optional.of(d))).defaultIfEmpty(Optional.empty())
                    .flatMap(optional -> {
                        // 取出body中的參數
                        String bodyString = "";
                        if (optional.isPresent()) {
                            byte[] oldBytes = new byte[optional.get().readableByteCount()];
                            optional.get().read(oldBytes);
                            bodyString = new String(oldBytes, StandardCharsets.UTF_8);
                        }
                        HttpHeaders httpHeaders = request.getHeaders();
                        // 執行XSS清理
                        log.debug("{} - [{}:{}] XSS處理前參數:{}", method, uri.getPath(), bodyString);
                        bodyString = XssUtil.INSTANCE.cleanXss(bodyString);
                        log.info("{} - [{}:{}] 參數:{}", method, uri.getPath(), bodyString);
                        
                        ServerHttpRequest newRequest = request.mutate().uri(uri).build();
                        // 重新構造body
                        byte[] newBytes = bodyString.getBytes(StandardCharsets.UTF_8);
                        DataBuffer bodyDataBuffer = toDataBuffer(newBytes);
                        Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
                        // 重新構造header
                        HttpHeaders headers = new HttpHeaders();
                        headers.putAll(httpHeaders);
                        // 由于修改了傳遞參數,需要重新設置CONTENT_LENGTH,長度是字節長度,不是字符串長度
                        int length = newBytes.length;
                        headers.remove(HttpHeaders.CONTENT_LENGTH);
                        headers.setContentLength(length);
                        headers.set(HttpHeaders.CONTENT_TYPE, "application/json;charset=utf8");
                        // 重寫ServerHttpRequestDecorator,修改了body和header,重寫getBody和getHeaders方法
                        newRequest = new ServerHttpRequestDecorator(newRequest) {
                            @Override
                            public Flux<DataBuffer> getBody() {
                                return bodyFlux;
                            }
                            @Override
                            public HttpHeaders getHeaders() {
                                return headers;
                            }
                        };
                        return chain.filter(exchange.mutate().request(newRequest).build());
                    });
        } else {
            return chain.filter(exchange);
        }
    }

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/u010044936/article/details/107067938

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 久久精品亚洲一区二区 | 一级黄色毛片免费观看 | 国产剧情一区二区 | 久久久久久久久久久久免费 | 精品在线91 | 中文字幕第33页 | 亚洲一区电影 | 欧美电影免费网站 | 九九导航 | 羞羞视频免费观看网站 | 成人国产精品视频 | 国产在线综合视频 | 午夜视频在线观看免费视频 | 国产精品一区二区无线 | 日韩中文字幕在线 | 亚洲精品第一区在线观看 | 91在线高清观看 | 久久精品二区 | 精品国产一区二区在线 | 亚洲一区视频网站 | 欧美精品影院 | 欧美日韩免费看 | 久久人人爽人人爽人人片av不 | 成人免费视频网 | 国产中文视频 | 一级毛片免费观看久 | 日韩成人在线网 | 精品一区二区三区免费视频 | 欧美精品乱码久久久久久按摩 | 不用播放器的毛片 | 在线亚洲精品 | 成人在线国产 | 欧美韩日 | 国内精品久久久久久 | 免费啪啪网站 | 四虎免费在线播放 | 成人日韩在线 | 日韩欧美一区二区三区 | 精品日韩一区二区三区 | 久久久久久久久久一区二区 | 国产精品片aa在线观看 |