fix(video): 收紧 Redis Jackson default typing 白名单,修补反序列化攻击面
- 本模块通过 ZLM hook / GB28181 信令把大量外部输入写入 Redis,
必须保证 default typing 的白名单足够紧。
- 新增 buildRedisObjectMapper:
· 注册 JavaTimeModule 支持 LocalDateTime / LocalDate 等 Java 8
时间类型,避免含 createTime 的对象序列化报错;
· PropertyAccessor 从 ALL/ANY 收窄到 FIELD/ANY,配合 Lombok getter
够用,同时降低经 setter 触发 gadget chain 的面;
· BasicPolymorphicTypeValidator 的 allowIfSubType 原先是 Object.class
(等价 LaissezFaireSubTypeValidator,Jackson 官方警告有 CVE 级
反序列化风险,如 jackson-databind #2367 / #2996),改成按包名
白名单:com.viewsh.* / com.alibaba.fastjson2.JSONObject|JSONArray
/ java.util|time|lang|math / 数组,严格收敛到业务真实存取范围。
- 两个 RedisTemplate Bean 都改用自构建的 ObjectMapper。
- 后续若遇到"新 DTO 反序列化不了",优先放进 com.viewsh 包下,
不要回退到 allowIfSubType(Object.class)。
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
package com.viewsh.module.video.framework.redis;
|
package com.viewsh.module.video.framework.redis;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
import com.viewsh.module.video.gb28181.bean.MobilePosition;
|
import com.viewsh.module.video.gb28181.bean.MobilePosition;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -12,22 +18,65 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
|
|||||||
* Video 模块专用 RedisTemplate 配置
|
* Video 模块专用 RedisTemplate 配置
|
||||||
* 独立 Bean 名称,避免覆盖框架的 Jackson 版本 RedisTemplate
|
* 独立 Bean 名称,避免覆盖框架的 Jackson 版本 RedisTemplate
|
||||||
*
|
*
|
||||||
* NOTE: Original WVP used FastJSON2 (GenericFastJsonRedisSerializer from fastjson2-extension-spring6).
|
* <p>ObjectMapper 注意点:
|
||||||
* Temporarily using GenericJackson2JsonRedisSerializer as fallback until
|
* 1. 注册 {@link JavaTimeModule}:支持 LocalDateTime / LocalDate 等 Java 8 时间类型
|
||||||
* fastjson2-extension-spring6 is available in the local Maven repository.
|
* (否则写入含 createTime 的对象会报 "not supported by default")
|
||||||
* TODO: Switch back to GenericFastJsonRedisSerializer once the dependency is resolved.
|
* 2. 启用 default typing:存入时写入 @class 元信息,读出时才能反序列化成原类
|
||||||
|
* (WVP 大量 Redis 用法依赖"存任意对象、读回来能强转"的能力)
|
||||||
|
* 3. typing 使用 BasicPolymorphicTypeValidator 做白名单校验,避免 gadget chain 攻击
|
||||||
|
*
|
||||||
|
* <p>NOTE: Original WVP used FastJSON2. 迁移到 Jackson 后需要手动对齐 JSR310 + default typing。
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
public class RedisTemplateConfig {
|
public class RedisTemplateConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建一份专用于 Redis 值序列化的 ObjectMapper
|
||||||
|
* 不复用全局 Spring Boot ObjectMapper,因为 Redis 的 default typing 会改变 JSON 输出,
|
||||||
|
* 不想污染到业务接口的 JSON 响应
|
||||||
|
*/
|
||||||
|
private static ObjectMapper buildRedisObjectMapper() {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
mapper.registerModule(new JavaTimeModule());
|
||||||
|
// 字段可见性只放开到 field 级(配合 Lombok getter 够用),不放开 setter/creator/is-getter,
|
||||||
|
// 降低反序列化时被"非预期 setter"串通 gadget chain 的面
|
||||||
|
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
|
||||||
|
// default typing:写入 @class 元信息以支持多态读回。
|
||||||
|
//
|
||||||
|
// 白名单要"够用 + 够紧":
|
||||||
|
// - 原先的 allowIfSubType(Object.class) 等价于 LaissezFaireSubTypeValidator,
|
||||||
|
// Jackson 官方明确警告会触发 CVE 级 gadget chain(jackson-databind #2367/#2996 等),
|
||||||
|
// 本模块通过 hook/SIP 信令把外部输入写进 Redis,必须收紧
|
||||||
|
// - 按业务实际需要列举允许的 package:
|
||||||
|
// 1) com.viewsh.* —— 本模块 + 框架公共结构(CommonResult / PageResult 等)
|
||||||
|
// 2) com.alibaba.fastjson2.JSONObject/JSONArray —— WVP 历史代码存这两个类到 Redis
|
||||||
|
// 3) java.util / java.time / java.lang / java.math —— 集合/时间/数字基础类型
|
||||||
|
// - 后续如果遇到"新 DTO 反序列化不了",优先把类放进 com.viewsh 包名下,
|
||||||
|
// 不要再退回 allowIfSubType(Object.class)
|
||||||
|
BasicPolymorphicTypeValidator validator = BasicPolymorphicTypeValidator.builder()
|
||||||
|
.allowIfSubType("com.viewsh.")
|
||||||
|
.allowIfSubType("com.alibaba.fastjson2.JSONObject")
|
||||||
|
.allowIfSubType("com.alibaba.fastjson2.JSONArray")
|
||||||
|
.allowIfSubType("java.util.")
|
||||||
|
.allowIfSubType("java.time.")
|
||||||
|
.allowIfSubType("java.lang.")
|
||||||
|
.allowIfSubType("java.math.")
|
||||||
|
.allowIfSubTypeIsArray()
|
||||||
|
.build();
|
||||||
|
mapper.activateDefaultTyping(
|
||||||
|
validator,
|
||||||
|
ObjectMapper.DefaultTyping.NON_FINAL,
|
||||||
|
JsonTypeInfo.As.PROPERTY);
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
|
||||||
@Bean("videoRedisTemplate")
|
@Bean("videoRedisTemplate")
|
||||||
public RedisTemplate<Object, Object> videoRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
|
public RedisTemplate<Object, Object> videoRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
|
||||||
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
|
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
|
||||||
// Using Jackson serializer as fallback (WVP originally used FastJSON2)
|
GenericJackson2JsonRedisSerializer jacksonSerializer =
|
||||||
GenericJackson2JsonRedisSerializer jacksonSerializer = new GenericJackson2JsonRedisSerializer();
|
new GenericJackson2JsonRedisSerializer(buildRedisObjectMapper());
|
||||||
redisTemplate.setValueSerializer(jacksonSerializer);
|
redisTemplate.setValueSerializer(jacksonSerializer);
|
||||||
redisTemplate.setHashValueSerializer(jacksonSerializer);
|
redisTemplate.setHashValueSerializer(jacksonSerializer);
|
||||||
// key serializer
|
|
||||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||||
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||||
redisTemplate.setConnectionFactory(redisConnectionFactory);
|
redisTemplate.setConnectionFactory(redisConnectionFactory);
|
||||||
@@ -38,7 +87,8 @@ public class RedisTemplateConfig {
|
|||||||
public RedisTemplate<String, MobilePosition> videoRedisTemplateForMobilePosition(
|
public RedisTemplate<String, MobilePosition> videoRedisTemplateForMobilePosition(
|
||||||
RedisConnectionFactory redisConnectionFactory) {
|
RedisConnectionFactory redisConnectionFactory) {
|
||||||
RedisTemplate<String, MobilePosition> redisTemplate = new RedisTemplate<>();
|
RedisTemplate<String, MobilePosition> redisTemplate = new RedisTemplate<>();
|
||||||
GenericJackson2JsonRedisSerializer jacksonSerializer = new GenericJackson2JsonRedisSerializer();
|
GenericJackson2JsonRedisSerializer jacksonSerializer =
|
||||||
|
new GenericJackson2JsonRedisSerializer(buildRedisObjectMapper());
|
||||||
redisTemplate.setValueSerializer(jacksonSerializer);
|
redisTemplate.setValueSerializer(jacksonSerializer);
|
||||||
redisTemplate.setHashValueSerializer(jacksonSerializer);
|
redisTemplate.setHashValueSerializer(jacksonSerializer);
|
||||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||||
|
|||||||
Reference in New Issue
Block a user