Spring Cloud Gateway 实现XSS、SQL注入拦截
创建Filter 实现GlobalFilter, Ordered@Slf4j@Componentpublic class SqLinjectionFilter implements GlobalFilter, Ordered {@SneakyThrows@Overridepublic Mono<Void> filter(ServerWebExchange exchange, Gatew
·
- 创建Filter 实现GlobalFilter, Ordered
@Slf4j
@Component
public class SqLinjectionFilter implements GlobalFilter, Ordered {
@SneakyThrows
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain){
// grab configuration from Config object
log.debug("----自定义防XSS攻击网关全局过滤器生效----");
ServerHttpRequest serverHttpRequest = exchange.getRequest();
HttpMethod method = serverHttpRequest.getMethod();
String contentType = serverHttpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
URI uri = exchange.getRequest().getURI();
Boolean postFlag = (method == HttpMethod.POST || method == HttpMethod.PUT) &&
(MediaType.APPLICATION_FORM_URLENCODED_VALUE.equalsIgnoreCase(contentType) || MediaType.APPLICATION_JSON_VALUE.equals(contentType));
//过滤get请求
if (method == HttpMethod.GET) {
String rawQuery = uri.getRawQuery();
if (StringUtils.isBlank(rawQuery)){
return chain.filter(exchange);
}
log.debug("原请求参数为:{}", rawQuery);
// 执行XSS清理
rawQuery = XssCleanRuleUtils.xssGetClean(rawQuery);
log.debug("修改后参数为:{}", rawQuery);
// 如果存在sql注入,直接拦截请求
if (rawQuery.contains("forbid")) {
log.error("请求【" + uri.getRawPath() + uri.getRawQuery() + "】参数中包含不允许sql的关键词, 请求拒绝");
return setUnauthorizedResponse(exchange);
}
try {
//重新构造get request
URI newUri = UriComponentsBuilder.fromUri(uri)
.replaceQuery(rawQuery)
.build(true)
.toUri();
ServerHttpRequest request = exchange.getRequest().mutate()
.uri(newUri).build();
return chain.filter(exchange.mutate().request(request).build());
} catch (Exception e) {
log.error("get请求清理xss攻击异常", e);
throw new IllegalStateException("Invalid URI query: \"" + rawQuery + "\"");
}
}
//post请求时,如果是文件上传之类的请求,不修改请求消息体
else if (postFlag){
return DataBufferUtils.join(serverHttpRequest.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 = serverHttpRequest.getHeaders();
// 执行XSS清理
log.debug("{} - [{}:{}] XSS处理前参数:{}", method, uri.getPath(), bodyString);
bodyString = XssCleanRuleUtils.xssPostClean(bodyString);
log.info("{} - [{}:{}] XSS处理后参数:{}", method, uri.getPath(), bodyString);
// 如果存在sql注入,直接拦截请求
if (bodyString.contains("forbid")) {
log.error("{} - [{}:{}] 参数:{}, 包含不允许sql的关键词,请求拒绝", method, uri.getPath(), bodyString);
return setUnauthorizedResponse(exchange);
}
ServerHttpRequest newRequest = serverHttpRequest.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);
}
}
// 自定义过滤器执行的顺序,数值越大越靠后执行,越小就越先执行
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
/**
* 设置403拦截状态
*/
private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange) {
return WebfluxResponseUtil.responseFailed(exchange, HttpStatus.FORBIDDEN.value(),
"request is forbidden, SQL keywords are not allowed in the parameters.");
}
/**
* 字节数组转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;
}
}
- 定义xss注入、sql注入工具类
@Slf4j
public class XssCleanRuleUtils {
private final static Pattern[] scriptPatterns = {
Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE),
Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),
Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL)
};
private static String badStrReg = "\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
private static Pattern sqlPattern = Pattern.compile(badStrReg, Pattern.CASE_INSENSITIVE);//整体都忽略大小写
/**
* GET请求参数过滤
* @param value
* @return
*/
public static String xssGetClean(String value) throws UnsupportedEncodingException {
//过滤xss字符集
if (value != null) {
value = value.replaceAll("\0|\n|\r", "");
for (Pattern pattern : scriptPatterns) {
value = pattern.matcher(value).replaceAll("");
}
value = value.replaceAll("<", "<").replaceAll(">", ">");
}
//sql关键字检查
return cleanGetSqlKeyWords(value);
}
public static String xssPostClean(String value) {
//过滤xss字符集
if (value != null) {
value = value.replaceAll("\0|\n|\r", "");
for (Pattern pattern : scriptPatterns) {
value = pattern.matcher(value).replaceAll("");
}
value = value.replaceAll("<", "<").replaceAll(">", ">");
}
//sql关键字检查
return cleanPostSqlKeyWords(value);
}
/**
* 解析参数SQL关键字
* @param value
* @return
*/
private static String cleanGetSqlKeyWords(String value) throws UnsupportedEncodingException {
//参数需要url编码
//这里需要将参数转换为小写来处理
//不改变原值
//value示例 order=asc&pageNum=1&pageSize=100&parentId=0
String lowerValue = URLDecoder.decode(value, "UTF-8").toLowerCase();
//获取到请求中所有参数值-取每个key=value组合第一个等号后面的值
boolean isContains = Stream.of(lowerValue.split("\\&"))
.map(kp -> kp.substring(kp.indexOf("=") + 1))
.parallel()
.anyMatch(param -> {
if (sqlPattern.matcher(param).find())
{
log.error("参数中包含不允许sql的关键词");
return true;
}
return false;
});
return isContains ? "forbid" : value;
}
/**
* 解析参数SQL关键字
* @param value
* @return
*/
private static String cleanPostSqlKeyWords(String value){
JSONObject json = JSONObject.parseObject(value);
Map<String, Object> map = json;
Map<String, Object> mapjson = new HashMap<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
String value1 = entry.getValue().toString();
//这里需要将参数转换为小写来处理-不改变原值
String lowerValue = value1.toLowerCase();
if (sqlPattern.matcher(lowerValue).find())
{
log.error("参数中包含不允许sql的关键词");
value1 = "forbid";
mapjson.put(entry.getKey(),value1);
break;
} else {
mapjson.put(entry.getKey(),entry.getValue());
}
}
return JSONObject.toJSONString(mapjson);
}
更多推荐
已为社区贡献7条内容
所有评论(0)