背景:
项目中要解决 Spring Security RegexRequestMatcher 认证绕过漏洞(CVE-2022-22978) 漏洞问题,并且需要将项目的版本整体升级到boot版本2.1.7,升级改造过程非常的痛苦,一方面对整个框架的代码不是很熟悉,另外对解决漏洞问题相对较少。
需求:
解决Spring Security RegexRequestMatcher 认证绕过漏洞(CVE-2022-22978) 漏洞,升级boot版本2.1.7
改造前父pom如下(部分依赖省略):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gisquest.realestate</groupId>
<artifactId>gisqrealestate-unityplatform-core</artifactId>
<version>2.2.1.RELEASE</version>
<modules>
<module>unityplatform-security</module>
<module>unityplatform-microauth</module>
<module>unityplatform-apigateway</module>
<module>unityplatform-servicediscovery</module>
<module>unityplatform-microappadmin</module>
<module>unityplatform-apigateway-core</module>
<module>unityplatform-apigateway-app</module>
<module>unityplatform-microauth-app</module>
<module>unityplatform-core-comm</module>
</modules>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot-admin.version>1.5.6</spring-boot-admin.version>
<jjwt.version>0.7.0</jjwt.version>
<commons-lang3.version>3.3.2</commons-lang3.version>
<swagger2.version>2.7.0</swagger2.version>
<apollo-client.version>1.0.0</apollo-client.version>
<jackson.version>2.10.2</jackson.version>
<!--micro version-->
<micro-apigateway.version>2.2.1.RELEASE</micro-apigateway.version>
<micro-auth.version>2.2.1.RELEASE</micro-auth.version>
<micro-comm.version>2.2.1.RELEASE</micro-comm.version>
<micro-apigateway.img.version>2.2.RELEASE</micro-apigateway.img.version>
<micro-auth.img.version>2.2.RELEASE</micro-auth.img.version>
<micro.img.version>2.2.RELEASE</micro.img.version>
<com.hanweb.SecurityUtil.version>1.0</com.hanweb.SecurityUtil.version>
<!--XStream -->
<xstream.version>1.4.20</xstream.version>
<spring.version>5.3.20</spring.version>
<spring-security.version>5.5.8</spring-security.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>Brussels-SR7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
漏洞解决方案
gisqrealestate-unityplatform-core 项目下 解决Spring Security RegexRequestMatcher 认证绕过漏洞(CVE-2022-22978) 漏洞,升级boot版本2.1.7(和快速构建保持一致),注意下升级之后一些配置文件(例如redis,文件限制大小等) 和 代码 不兼容问题
漏洞影响版本
5.5.0 <= Spring Security < 5.5.7
5.6.0 <= Spring Security < 5.6.4
版本改造问题:
改造前 版本
<spring-cloud.version>Edgware.SR2</spring-cloud.version> <spring-boot.version>1.5.10.RELEASE </spring-boot.version>
升级到2.x版本
<spring-cloud.version>Greenwich.SR2</spring-cloud.version> <spring-boot.version>2.1.7.RELEASE</spring-boot.version>
问题一:项目改造前是自定义的springboot依赖,现要求去掉原先的自定义封装依赖
刚开始我是把原先自定义的springboot依赖的父依赖直接移到最外层,大概过程如下,导致出现一堆问题。
坑一:类不存在,或包不存在
项目中出现大量的类不存在,但是明明可以用鼠标点击进去,此类问题经常是idea,什么缓存,或者是pom依赖下载不全导致,然后就陷入了这个误区,一直以为是idea的问题。
先是清理 idea缓存、执行mvn idea:idea命令等方式很多次还是无法解决。
然后分支代码提交后,同事检出也是同样的问题,项目还是无法启动,保错也是提示类不存在等。
此时我就怀疑可能不是idea的问题了。
后面就换了一种方式
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.10.RELEASE</version> <relativePath /> </parent>
改成
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency>
妈的改成这样后终于正常了,不在提示类不存在,包不存在等问题了。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<groupId>com.gisquest.realestate</groupId>
<artifactId>gisqrealestate-unityplatform-core</artifactId>
<version>2.2.1.RELEASE</version>
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<modules>
<module>unityplatform-security</module>
<module>unityplatform-microauth</module>
<module>unityplatform-apigateway</module>
<module>unityplatform-servicediscovery</module>
<module>unityplatform-microappadmin</module>
<module>unityplatform-apigateway-core</module>
<module>unityplatform-apigateway-app</module>
<module>unityplatform-microauth-app</module>
<module>unityplatform-core-comm</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot-admin.version>1.5.6</spring-boot-admin.version>
<jjwt.version>0.7.0</jjwt.version>
<commons-lang3.version>3.3.2</commons-lang3.version>
<swagger2.version>2.7.0</swagger2.version>
<apollo-client.version>1.0.0</apollo-client.version>
<jackson.version>2.10.2</jackson.version>
<!--micro version-->
<micro-apigateway.version>2.2.1.RELEASE</micro-apigateway.version>
<micro-auth.version>2.2.1.RELEASE</micro-auth.version>
<micro-comm.version>2.2.1.RELEASE</micro-comm.version>
<micro-apigateway.img.version>2.2.RELEASE</micro-apigateway.img.version>
<micro-auth.img.version>2.2.RELEASE</micro-auth.img.version>
<micro.img.version>2.2.RELEASE</micro.img.version>
<com.hanweb.SecurityUtil.version>1.0</com.hanweb.SecurityUtil.version>
<!--XStream -->
<xstream.version>1.4.19</xstream.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
<spring-boot.version>2.1.7.RELEASE</spring-boot.version>
<eureka.version>2.1.0.RELEASE</eureka.version>
<openfeign.version>2.0.0.RELEASE</openfeign.version>
<zuul.version>2.1.2.RELEASE</zuul.version>
<commons-lang.version>2.6</commons-lang.version>
<commons-collections.version>3.2.2</commons-collections.version>
<commons-beanutils.version>1.9.3</commons-beanutils.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</project>
坑二:idea中maven设置,最好调整成maven运行(推荐)
否则会出现稀奇古怪的问题,比如上面的之前用parent依赖方式我本地可以跑(奇了怪),但是同事拉下拉却跑不了,切换成eclipse也同样项目跑不起来。
经过一番折腾代码是跑起来了总算不提示类不存等问题。
代码兼容问题
org.springframework.data.redis.serializer.SerializationException: Cannot deserialize
org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.InvalidClassException: org.springframework.security.core.authority.SimpleGrantedAuthority; local class incompatible: stream classdesc serialVersionUID = 420, local class serialVersionUID = 510
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:84) ~[spring-data-redis-2.1.10.RELEASE.jar!/:2.1.10.RELEASE]
解决方法:
redis跨版本升级后,需要把redis缓存清理一下
问题二:alibaba.fastjson.JSONObject cannot be cast to xx
java.lang.ClassCastException: com.alibaba.fastjson.JSONObject cannot be cast to com.gisquest.realestate.security.validate.code.ValidateCode
改造前逻辑
@Override
public ValidateCode get(ServletWebRequest request, ValidateCodeType validateCodeType) {
Object object = redisTemplate.opsForValue().get(buildKey(request, validateCodeType));
if (object == null) {
return null;
}
return (ValidateCode)object ;
}
改造后
@Override
public ValidateCode get(ServletWebRequest request, ValidateCodeType validateCodeType) {
Object object = redisTemplate.opsForValue().get(buildKey(request, validateCodeType));
if (object == null) {
return null;
}
String josn =JSON.toJSONString(object);
return JSONObject.parseObject(josn,ValidateCode.class);
}
问题解决!!!!
问题三: 升级后通过feign调用认证服务接口/oauth/check_token接口提示如下 Illegal hexadecimal character - at index 8
org.apache.commons.codec.DecoderException: Illegal hexadecimal character - at index 8
at org.apache.commons.codec.binary.Hex.toDigit(Hex.java:286)
at org.apache.commons.codec.binary.Hex.decodeHex(Hex.java:106)
at com.gisquest.realestate.utils.Encodes.decodeHex(Encodes.java:41)
原因版本问题
改造前spring-security-core-4.2.4.RELEASE
在AbstractUserDetailsAuthenticationProvider在实现类中additionalAuthenticationChecks方法逻辑如下
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
Object salt = null;
if (this.saltSource != null) {
salt = this.saltSource.getSalt(userDetails);
}
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
String presentedPassword = authentication.getCredentials().toString();
if (!passwordEncoder.isPasswordValid(userDetails.getPassword(),
presentedPassword, salt)) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
改造后
spring-security-core-5.1.6.RELEASE版本中AbstractUserDetailsAuthenticationProvider在实现类中改方法additionalAuthenticationChecks
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials() == null) {
this.logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
} else {
String presentedPassword = authentication.getCredentials().toString();
if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
this.logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
}
逻辑变化了
使用了自定义的密码加密类
密码加密方式发现不一样了,因此按照原先代码逻辑获取到的前后密码其实是一样的,但是升级后GisqPasswordEncoder起了作用,因此在比较时应该时加密后的匹配
因此改成这样
问题四:feign调用token校验接口,在项目中未搜索到,
后来打断点才发现竟然后spring-security-oaruth2包内部类CheckTokenEndpoint中的接口,无语,我还以为是自己定义的呢找了半天未找打!!!!!
jar包依赖冲突问题:
方式一:选择依赖的模块,鼠标右键,选择show dependency
,如果有冲突会出现报红!!!
方式二:安装一个maven-helper插件
如果有冲突会自动显示,然后去排处即可