springcloud3(七) 安全框架Hdiv
Hdiv Security 是支持应用程序自我保护的先驱,是同类产品中的第一款产品,可在整个软件开发生命周期 (SDLC) 中提供针对安全漏洞和业务逻辑缺陷的保护。Hdiv Security 的统一平台使 DevSecOps 成为现实。Hdiv 的解决方案目前被政府、银行、航空航天和财富 500 强公司使用。官方网站: hdivsecurity.com.
目前Hdiv分为商业版和社区版,但是网上google和百度能够查到资料很少,但是可以在hdiv-config这个包基础上做二次开发
1. hdiv社区版,默认能够做哪些安全防护
1.1 SQL注入攻击 (SQL Injection)
1.2 代码执行攻击 (Code execution)
1.3 跨站脚本攻击(XSS)
包含简单的跨站脚本攻击, 图片攻击,script标签攻击,eval攻击
关于常见的攻击方式,这篇博客写的比较全: https://blog.csdn.net/tushanpeipei/article/details/120382711
2. spring cloud gateway + hdiv-config 实现的demo
<dependency> <groupId>org.hdiv</groupId> <artifactId>hdiv-config</artifactId> <version>3.4.0</version> </dependency>
hdiv-config的安全保护逻辑在defaultEditableValidations.xml中,即正则表达式中
<?xml version="1.0" encoding="UTF-8"?> <defaultValidations> <!-- SQL Injection attacks detection validation rule --> <validation id="SQLInjection"> <![CDATA[(\s|\S)*((%27)|(')|(%3D)|(=)|(%2F)|(")|((%22)|(-|%2D){2})|(%23)|(%3B)|(;))+(\s|\S)*]]> </validation> <!-- Code execution attacks detection validation rule --> <validation id="execCommand"> <![CDATA[(\s|\S)*(exec(\s|\+)+(s|x)p\w+)(\s|\S)*]]> </validation> <!-- XSS attacks detection validation rules --> <validation id="simpleXSS"> <![CDATA[(\s|\S)*((%3C)|<)((%2F)|/)*[a-z0-9%]+((%3E)|>)(\s|\S)*]]> </validation> <validation id="imageXSS"> <![CDATA[(\s|\S)*((%3C)|<)((%69)|i|I|(%49))((%6D)|m|M|(%4D))((%67)|g|G|(%47))[^\n]+((%3E)|>)(\s|\S)*]]> </validation> <validation id="scriptXSS"> <![CDATA[(\s|\S)*((%73)|s)(\s)*((%63)|c)(\s)*((%72)|r)(\s)*((%69)|i)(\s)*((%70)|p)(\s)*((%74)|t)((\s)|(\:)){1}(\s|\S)*]]> </validation> <validation id="evalXSS"> <![CDATA[(\s|\S)*((%65)|e)(\s)*((%76)|v)(\s)*((%61)|a)(\s)*((%6C)|l)(\s)*(\()(\s|\S)*]]> </validation> </defaultValidations>
上面的%加字符的是ASCII码,即一个百分号%后面跟对应字符的ASCII(16进制)码值。如果不使用转义字符,这些编码就会当URL中定义的特殊字符处理。常见的URL特殊符号及编码十六进制值可以看下这篇博客:https://blog.csdn.net/WuLex/article/details/98850868
我这边demo的逻辑其实很简单就是在gateway的filter中对请求的header和body进行以上正则表达式的校验
package com.kawa.spbgateway.filter; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.sun.org.apache.xerces.internal.impl.Constants; import lombok.extern.slf4j.Slf4j; import org.hdiv.config.validations.DefaultValidationParser; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.codec.CodecProperties; import org.springframework.boot.context.properties.PropertyMapper; 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.http.MediaType; import org.springframework.http.codec.CodecConfigurer; import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.util.unit.DataSize; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.server.ServerWebExchange; import org.xml.sax.SAXException; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @Slf4j @Component public class InjectProtectFilter implements GlobalFilter, Ordered { private List<Pattern> hdivRules = new ArrayList<>(); DefaultValidationParser parser = new DefaultValidationParser(); private ObjectMapper objectMapper; private List<HttpMessageReader<?>> messageReaders; public InjectProtectFilter(@Value("${gateway.inject-protect.file:}") String validationFilePath, ObjectMapper objectMapper, CodecConfigurer codecConfigurer, CodecProperties codecProperties) { log.info(">>>>>>>>>> InjectProtectFilter"); if (StringUtils.hasText(validationFilePath)) { readValidations(validationFilePath, parser); } else { parser.readDefaultValidations(); } List<Map<DefaultValidationParser.ValidationParam, String>> validations = parser.getValidations(); validations.forEach(val -> { String regex = val.get(DefaultValidationParser.ValidationParam.REGEX); hdivRules.add(Pattern.compile(regex)); }); this.objectMapper = objectMapper; this.messageReaders = fetchMessageReaders(codecConfigurer, codecProperties); } private List<HttpMessageReader<?>> fetchMessageReaders(CodecConfigurer codecConfigurer, CodecProperties codecProperties) { PropertyMapper propertyMapper = PropertyMapper.get(); CodecConfigurer.DefaultCodecs defaultCodecs = codecConfigurer.defaultCodecs(); propertyMapper .from(codecProperties.getMaxInMemorySize()) .whenNonNull() .asInt(DataSize::toBytes) .to(defaultCodecs::maxInMemorySize); return codecConfigurer.getReaders(); } /** * prevent external entities operate * * @param validationFilePath * @param parser */ private void readValidations(String validationFilePath, DefaultValidationParser parser) { try (var fis = new FileInputStream(validationFilePath)) { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setFeature(Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE, false); spf.setFeature(Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE, false); spf.setFeature(Constants.SAX_FEATURE_PREFIX + Constants.DISALLOW_DOCTYPE_DECL_FEATURE, true); SAXParser sp = spf.newSAXParser(); sp.parse(fis, parser); } catch (IOException | ParserConfigurationException | SAXException e) { e.printStackTrace(); } } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { long contentLength = exchange.getRequest().getHeaders().getContentLength(); MediaType contentType = exchange.getRequest().getHeaders().getContentType(); List<String> headers = exchange.getRequest().getHeaders().values().stream().flatMap(list -> list.stream()).collect(Collectors.toList()); log.info(">>>>>>>>>> InjectProtectFilter-filter request headers: {}", headers); if (contentLength > 0 && (MediaType.APPLICATION_JSON.equals(contentType))) { return DataBufferUtils .join(exchange.getRequest().getBody()) .flatMap(dataBuffer -> validateJson(exchange, chain, dataBuffer, headers)); } validateParamList(headers); return chain.filter(exchange); } private boolean validateParam(String paramStr, Pattern rule) { Matcher matcher = rule.matcher(paramStr); return matcher.matches(); } private Mono<? extends Void> validateJson(ServerWebExchange exchange, GatewayFilterChain chain, DataBuffer dataBuffer, List<String> params) { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); DataBufferUtils.release(dataBuffer); Flux<DataBuffer> cacheFlux = Flux.defer(() -> { DataBuffer wrap = exchange.getResponse().bufferFactory().wrap(bytes); DataBufferUtils.retain(wrap); return Mono.just(wrap); }); ServerHttpRequest serverHttpRequest = new ServerHttpRequestDecorator(exchange.getRequest()) { @Override public Flux<DataBuffer> getBody() { return cacheFlux; } }; ServerWebExchange serverWebExchange = exchange.mutate().request(serverHttpRequest).build(); return ServerRequest.create(serverWebExchange, messageReaders) .bodyToMono(String.class) .doOnNext(bodyStr -> { try { JsonNode jsonNode = objectMapper.readValue(bodyStr, JsonNode.class); add2ParamList(jsonNode, params); } catch (JsonProcessingException e) { log.info(">>>>>>>>> invalid json body: {}", bodyStr); } validateParamList(params); }).then(chain.filter(serverWebExchange)); } private void add2ParamList(JsonNode jsonNode, List<String> params) { if (jsonNode.isObject()) { ObjectNode objNode = (ObjectNode) jsonNode; objNode.fields().forEachRemaining(val -> add2ParamList(val.getValue(), params)); } else if (jsonNode.isArray()) { ArrayNode arrayNode = (ArrayNode) jsonNode; arrayNode.forEach(node -> add2ParamList(node, params)); } else { params.add(jsonNode.asText()); } } private void validateParamList(List<String> params) { Iterator<String> paramIterator = params.iterator(); stop: while (paramIterator.hasNext()) { String param = paramIterator.next(); Iterator<Pattern> ruleIterator = hdivRules.iterator(); while (ruleIterator.hasNext()) { Pattern rule = ruleIterator.next(); if (validateParam(param, rule)) { log.error(">>>>>>>>>> hit the security rule, param:{} rule:{}", param, rule); // throw exception break stop; } } } } @Override public int getOrder() { return Integer.MIN_VALUE + 99; } }
测试demo
分类:
Spring
标签:
springcloud gateway
, hdiv
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
2021-01-13 Chrome常用快捷键汇总