Compare commits

...

5 Commits

Author SHA1 Message Date
阿斌
76f030987f
Pre Merge pull request !46 from 阿斌/N/A 2026-05-28 07:31:59 +00:00
648540858
875c4aed6d
Merge pull request #2161 from Gerrit1999/dev/2159
fix: 修复 issue 2159 级联点播自定义 SSRC 鉴权 key 未刷新问题
2026-05-28 14:50:12 +08:00
lin
2fc63caf13 SSRC管理-加快SSRC重建频率 2026-05-28 14:42:09 +08:00
gerrit
75c38d6c9a
fix: 级联点播下级自定义SSRC后同步刷新RTP鉴权key
当 use-custom-ssrc-for-parent-invite=true 时,下级平台可在 200 OK SDP
中返回自定义 SSRC。此前仅在开收流时按原始 SSRC 写入一次 Redis 鉴权
key,导致 ZLM on_publish 以实际 SSRC 查 Redis 时鉴权失败。

新增 IReceiveRtpServerService.refreshAuthenticateInfo(),在
PlayServiceImpl.InviteOKHandler 解析出最终 SSRC 后,如与原始值不
一致,先刷新 Redis 鉴权 key,再继续现有 SSRC 更新流程。覆盖点播、
回放、下载三种场景。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 14:29:15 +08:00
阿斌
c011389c3f
SDP 注入攻击 / 非法 SDP 协议数据
java.text.ParseException: [C@2f1fec26
ID expected
        at gov.nist.core.LexerCore.match(LexerCore.java:229)
        at gov.nist.javax.sdp.parser.OriginFieldParser.originField(OriginFieldParser.java:90)
        at gov.nist.javax.sdp.parser.OriginFieldParser.parse(OriginFieldParser.java:108)
        at gov.nist.javax.sdp.parser.SDPAnnounceParser.parse(SDPAnnounceParser.java:113)
        at javax.sdp.SdpFactory.createSessionDescription(SdpFactory.java:129)
        at com.genersoft.iot.vmp.gb28181.utils.SipUtils.parseSDP(SipUtils.java:229)
        at com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.InviteRequestProcessor.decode(InviteRequestProcessor.java:275)
        at com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.InviteRequestProcessor.process(InviteRequestProcessor.java:125)
        at com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver.processRequest(SIPProcessorObserver.java:71)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
        at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:114)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
        at java.base/java.lang.Thread.run(Thread.java:1583)
java.text.ParseException: o=- ' OR 'a'='a'; -- 1 IN IP4 179.43.150.26
        at gov.nist.javax.sdp.parser.OriginFieldParser.originField(OriginFieldParser.java:103)
        at gov.nist.javax.sdp.parser.OriginFieldParser.parse(OriginFieldParser.java:108)
        at gov.nist.javax.sdp.parser.SDPAnnounceParser.parse(SDPAnnounceParser.java:113)
        at javax.sdp.SdpFactory.createSessionDescription(SdpFactory.java:129)
        at com.genersoft.iot.vmp.gb28181.utils.SipUtils.parseSDP(SipUtils.java:229)
        at com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.InviteRequestProcessor.decode(InviteRequestProcessor.java:275)
        at com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.InviteRequestProcessor.process(InviteRequestProcessor.java:125)
        at com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver.processRequest(SIPProcessorObserver.java:71)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
        at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:114)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
        at java.base/java.lang.Thread.run(Thread.java:1583)


Signed-off-by: 阿斌 <38912748@qq.com>
2026-03-21 16:15:47 +00:00
5 changed files with 42 additions and 5 deletions

View File

@ -859,6 +859,9 @@ public class PlayServiceImpl implements IPlayService {
}
}else {
log.info("[Invite 200OK] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
String oldStreamId = String.format("%08x", Long.parseLong(ssrcInfo.getSsrc())).toUpperCase();
String newStreamId = String.format("%08x", Long.parseLong(ssrcInResponse)).toUpperCase();
receiveRtpServerService.refreshAuthenticateInfo(oldStreamId, newStreamId);
// ssrc 不一致
if (mediaServerItem.isRtpEnable()) {
// 多端口

View File

@ -50,7 +50,7 @@ public class SSRCFactory {
public void init() {
String sipDomain = sipConfig.getDomain();
domainPart = sipDomain.length() >= 8 ? sipDomain.substring(3, 8) : sipDomain;
scheduler.scheduleAtFixedRate(this::rebuild, 10, 30, TimeUnit.SECONDS);
scheduler.scheduleAtFixedRate(this::rebuild, 5, 5, TimeUnit.SECONDS);
}
public String getPlaySsrc(String mediaServerId) {
@ -127,7 +127,7 @@ public class SSRCFactory {
usedMap.put(server.getId(), bits);
if (count > 8000) {
log.info("[SSRC重建] 媒体节点 {} 的SSRC使用率已超过80%,请注意扩展服务提升性能", server.getId());
} else {
}
if (log.isDebugEnabled()) {
log.debug("[SSRC重建] 节点 {} 已占用 {} 个SSRC", server.getId(), count);
}
@ -135,4 +135,3 @@ public class SSRCFactory {
}
}
}
}

View File

@ -206,6 +206,21 @@ public class SipUtils {
public static Gb28181Sdp parseSDP(String sdpStr) throws SdpParseException {
// 校验拦截空内容与注入攻击特征
if (sdpStr == null || sdpStr.trim().isEmpty()) {
throw new SdpParseException(0, 0, "SDP内容为空");
}
// 标准SDP每行格式固定为 "x=value"不存在SQL关键字出现则视为注入攻击
String sdpUpper = sdpStr.toUpperCase();
if (sdpUpper.contains("' OR '") || sdpUpper.contains("' OR 1") || sdpUpper.contains(" OR 1=1")
|| sdpUpper.contains("--") || sdpUpper.contains("/*") || sdpUpper.contains("*/")
|| sdpUpper.contains("DROP ") || sdpUpper.contains("INSERT ") || sdpUpper.contains("UPDATE ")
|| sdpUpper.contains("DELETE ") || sdpUpper.contains("UNION ") || sdpUpper.contains("SELECT ")) {
log.error("[SDP注入攻击] 检测到非法SDP内容已拒绝解析内容长度: {}", sdpStr.length());
throw new SdpParseException(0, 0, "非法SDP内容");
}
//校验结束
// jainSip不支持y= f=字段 移除以解析
int ssrcIndex = sdpStr.indexOf("y=");
int mediaDescriptionIndex = sdpStr.indexOf("f=");

View File

@ -39,4 +39,6 @@ public interface IReceiveRtpServerService {
void addAuthenticateInfo(String streamId, String streamReplace, Boolean enableAudio, Boolean enableMp4, Integer mp4MaxSecond);
ResultForOnPublish getAuthenticateInfo(String streamId);
void refreshAuthenticateInfo(String oldStreamId, String newStreamId);
}

View File

@ -406,4 +406,22 @@ public class RtpServerServiceImpl implements IReceiveRtpServerService {
}
return null;
}
@Override
public void refreshAuthenticateInfo(String oldStreamId, String newStreamId) {
if (oldStreamId == null || newStreamId == null || oldStreamId.equals(newStreamId)) {
return;
}
String oldKey = String.format("%s:%s", VideoManagerConstants.RTP_AUTHENTICATE, oldStreamId);
Object obj = redisTemplate.opsForValue().get(oldKey);
if (obj instanceof ResultForOnPublish) {
String newKey = String.format("%s:%s", VideoManagerConstants.RTP_AUTHENTICATE, newStreamId);
redisTemplate.opsForValue().set(newKey, obj);
redisTemplate.expire(newKey, 60, TimeUnit.SECONDS);
redisTemplate.delete(oldKey);
log.info("[刷新RTP鉴权信息] {} -> {}", oldStreamId, newStreamId);
} else {
log.warn("[刷新RTP鉴权信息] 未找到旧key: {}", oldKey);
}
}
}