文章目录
- 0.前言
- 漏洞
- 受影响的Spring产品和版本
- 1.参考文档
- 2.基础介绍
- 描述
- 3.解决方案
- 3.1. 升级版本
- 4.HeaderHttpSessionIdResolver 解析
- 5. Spring Session 使用教程
0.前言
背景:公司项目扫描到 CVE-2023-20866:在Spring Session中会将会话ID记录到标准输出流
漏洞
中风险 | 2023年4月12日 | CVE-2023-20866
描述
在Spring Session 3.0.0版本中,会将会话ID记录到标准输出流中。此漏洞将敏感信息暴露给那些可以访问应用程序日志的人,可能被用于会话劫持。
描述
在Spring Session 3.0.0版本中,会将会话ID记录到标准输出流中。此漏洞将敏感信息暴露给那些可以访问应用程序日志的人,可能被用于会话劫持。
具体而言,使用了HeaderHttpSessionIdResolver,应用程序存在漏洞。
未使用HeaderHttpSessionIdResolver。则应用程序不受影响
受影响的Spring产品和版本
Spring Session 3.0.0
1.参考文档
CVE 官方网站 https://www.cve.org/CVERecord?id=CVE-2023-20866
spring官方网站 https://spring.io/security/cve-2023-20866
2.基础介绍
CVE-2023-20866:在Spring Session中会将会话ID记录到标准输出流
中风险 | 2023年4月12日 | CVE-2023-20866
描述
在Spring Session 3.0.0版本中,会将会话ID记录到标准输出流中。此漏洞将敏感信息暴露给那些可以访问应用程序日志的人,可能被用于会话劫持。
具体而言,当满足以下条件时,应用程序存在漏洞:
-
使用了HeaderHttpSessionIdResolver。
未使用HeaderHttpSessionIdResolver。则应用程序不受影响:
受影响的Spring产品和版本
Spring Session 3.0.0
3.解决方案
3.1. 升级版本
受影响版本的用户应升级到Spring Session 3.0.1。
4.HeaderHttpSessionIdResolver 解析
HeaderHttpSessionIdResolver
是 HttpSessionIdResolver
的一个实现,它负责从 HTTP 请求和响应的头部中解析和写入会话 ID。
以下是 HeaderHttpSessionIdResolver
的主要方法的源码解析:
- 构造函数
public HeaderHttpSessionIdResolver(String headerName) {
Assert.hasText(headerName, "headerName must not be empty");
this.headerName = headerName;
}
构造函数接收一个字符串参数 headerName
,这个参数表示会话 ID 在 HTTP 头部的键。这个函数首先检查 headerName
是否为空,如果为空则抛出异常。然后,将 headerName
保存到类的私有字段中。
resolveSessionIds
方法
@Override
public List<String> resolveSessionIds(HttpServletRequest request) {
String sessionId = request.getHeader(this.headerName);
return (sessionId != null ? Collections.singletonList(sessionId) : Collections.emptyList());
}
resolveSessionIds
方法从 HTTP 请求的头部中获取会话 ID。如果找到了会话 ID,那么返回一个只包含这个会话 ID 的列表。如果没有找到会话 ID,那么返回一个空的列表。
setSessionId
方法
@Override
public void setSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId) {
response.setHeader(this.headerName, sessionId);
}
setSessionId
方法将会话 ID 写入到 HTTP 响应的头部中。这样,客户端在接收到响应后可以从头部中获取到会话 ID。
expireSession
方法
@Override
public void expireSession(HttpServletRequest request, HttpServletResponse response) {
response.setHeader(this.headerName, "");
}
expireSession
方法将 HTTP 响应的头部中的会话 ID 设置为空字符串。这样,客户端在接收到响应后会认为会话已经过期。
HeaderHttpSessionIdResolver
类的实现非常简单,但是它却是 Spring Session 中关于会话管理的重要组成部分。
5. Spring Session 使用教程
Spring Session 提供 API 和实现,用于在任何分布式应用程序中管理用户会话信息。会话可以在多个节点之间共享,这对于构建高可用、可扩展的应用程序非常有用。
- 添加Spring Session和相应存储类型的依赖项
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
这里使用的是 Redis 作为会话信息的存储。
- 配置Spring Session:
@EnableRedisHttpSession
public class HttpSessionConfig {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
}
此处 @EnableRedisHttpSession
注解启用了 Spring Session 支持,并创建了一个LettuceConnectionFactory Bean用来连接Redis。
- 使用Spring Session:
@Controller
public class HelloController {
@RequestMapping("/setSession")
@ResponseBody
public String setSession(HttpServletRequest request) {
request.getSession().setAttribute("message", "Hello Spring Session");
return "Session Set";
}
@RequestMapping("/getSession")
@ResponseBody
public String getSession(HttpServletRequest request) {
return (String) request.getSession().getAttribute("message");
}
}
setSession()
方法将一个属性存储到会话中,getSession()
方法从会话中获取该属性。
- 确保你的应用程序能够连接到Redis服务器,这样Spring Session就可以正常工作。