6.8 KiB
6.8 KiB
SuperGroup (GroupType=1) 使用限制分析
一、核心问题
如果绕过创建限制,创建了 GroupType=1 (SuperGroup) 的群组,在使用上会有以下关键差异和潜在问题:
二、关键差异点
2.1 消息验证逻辑差异 ⚠️ 重要
位置: internal/rpc/msg/verify.go:187-220
SuperGroup (GroupType=1) 的特殊处理:
if groupInfo.GroupType == constant.SuperGroup {
// SuperGroup 跳过大部分检查,但仍需检查文件权限
if data.MsgData.ContentType == constant.File {
// 只检查文件发送权限
}
return nil // 直接返回,跳过后续所有检查
}
WorkingGroup (GroupType=2) 的完整验证:
- ✅ 检查用户是否在群内
- ✅ 检查文件发送权限(群主/管理员/userType=1)
- ✅ 检查链接发送权限(userType=1/群主/管理员可发送链接)
- ✅ 检查二维码图片(userType=0 普通成员不能发送包含二维码的图片)
- ✅ 检查禁言状态(MuteEndTime)
- ✅ 检查群组是否被禁言(GroupStatusMuted)
差异总结:
| 检查项 | SuperGroup | WorkingGroup |
|---|---|---|
| 文件发送权限 | ✅ 检查 | ✅ 检查 |
| 链接检测 | ❌ 跳过 | ✅ 检查 |
| 二维码检测 | ❌ 跳过 | ✅ 检查 |
| 禁言检查 | ❌ 跳过 | ✅ 检查 |
| 群组禁言检查 | ❌ 跳过 | ✅ 检查 |
⚠️ 风险:
- SuperGroup 可以发送包含链接的消息,即使 userType=0
- SuperGroup 可以发送包含二维码的图片,即使 userType=0
- SuperGroup 成员即使被禁言也可以发送消息
- SuperGroup 即使群组被禁言,成员仍可发送消息
2.2 消息推送机制
位置: internal/push/push_handler.go:104-116
推送逻辑:
switch msgFromMQ.MsgData.SessionType {
case constant.ReadGroupChatType:
err = c.Push2Group(ctx, msgFromMQ.MsgData.GroupID, msgFromMQ.MsgData)
default:
err = c.Push2User(ctx, pushUserIDList, msgFromMQ.MsgData)
}
关键发现:
- 推送逻辑不依赖 GroupType,而是依赖 SessionType
SessionType是客户端发送消息时指定的,不是根据 GroupType 自动确定的- 如果客户端发送消息时
SessionType != ReadGroupChatType,会走Push2User而不是Push2Group
⚠️ 潜在问题:
- 如果创建了
GroupType=1的群组,但客户端发送消息时使用SessionType=WriteGroupChatType(值为2),消息会按单聊方式推送,可能导致推送逻辑异常 Push2Group和Push2User的推送机制不同,可能影响消息分发
2.3 会话ID生成差异
位置: pkg/msgprocessor/conversation.go:68-97
会话ID生成规则:
case constant.WriteGroupChatType:
return "g_" + msg.GroupID // 普通群聊
case constant.ReadGroupChatType:
return "sg_" + msg.GroupID // 超级群聊
差异:
WriteGroupChatType(值为2): 会话ID前缀为g_ReadGroupChatType(值为3): 会话ID前缀为sg_
⚠️ 潜在问题:
- 如果
GroupType=1但使用SessionType=WriteGroupChatType,会话ID会是g_xxx而不是sg_xxx - 这可能导致会话管理不一致
2.4 在线推送方法
位置: internal/push/onlinepusher.go:99
推送方法:
- 所有群聊消息(无论 GroupType)都使用
SuperGroupOnlineBatchPushOneMsg方法 - 方法名虽然叫 "SuperGroup",但实际上所有群聊都使用这个方法
结论:推送方法本身不受 GroupType 影响,但推送逻辑受 SessionType 影响
三、使用限制和潜在错误
3.1 必须使用正确的 SessionType ⚠️ 关键
问题:如果创建了 GroupType=1 的群组,但发送消息时:
- ❌ 使用
SessionType=WriteGroupChatType(值为2) → 会走Push2User,推送逻辑可能异常 - ✅ 必须使用
SessionType=ReadGroupChatType(值为3) → 会走Push2Group,推送正常
建议:客户端发送消息时,需要根据群组的 GroupType 自动设置正确的 SessionType:
GroupType=1(SuperGroup) →SessionType=ReadGroupChatType(值为3)GroupType=2(WorkingGroup) →SessionType=WriteGroupChatType(值为2) 或ReadGroupChatType(值为3)
3.2 权限检查缺失 ⚠️ 安全风险
SuperGroup 跳过的检查:
- 链接检测:普通成员可以发送包含链接的消息
- 二维码检测:普通成员可以发送包含二维码的图片
- 禁言检查:被禁言的成员仍可发送消息
- 群组禁言检查:群组被禁言时,成员仍可发送消息
安全影响:
- 可能被用于发送恶意链接
- 可能被用于发送包含二维码的垃圾信息
- 禁言功能失效
3.3 会话管理不一致
问题:
- 如果
GroupType=1但使用SessionType=WriteGroupChatType,会话ID会是g_xxx - 如果使用
SessionType=ReadGroupChatType,会话ID会是sg_xxx - 这可能导致同一个群组产生不同的会话ID,造成会话管理混乱
四、总结
4.1 主要差异
| 方面 | SuperGroup (GroupType=1) | WorkingGroup (GroupType=2) |
|---|---|---|
| 消息验证 | 跳过大部分检查(链接、二维码、禁言) | 完整验证 |
| 文件权限 | 检查(群主/管理员/userType=1) | 检查(群主/管理员/userType=1) |
| 推送机制 | 依赖 SessionType,需使用 ReadGroupChatType | 依赖 SessionType |
| 会话ID | 需使用 ReadGroupChatType 生成 sg_ 前缀 | 可使用 WriteGroupChatType 或 ReadGroupChatType |
4.2 使用建议
如果允许创建 SuperGroup 类型的群组,需要:
-
客户端适配:
- 发送消息时,根据群组的
GroupType自动设置正确的SessionType GroupType=1→SessionType=ReadGroupChatType(值为3)
- 发送消息时,根据群组的
-
安全考虑:
- 明确 SuperGroup 跳过权限检查的设计意图
- 如果需要安全控制,考虑在业务层或回调中实现
-
一致性:
- 确保所有使用 SuperGroup 的地方都使用
ReadGroupChatType - 统一会话ID生成规则
- 确保所有使用 SuperGroup 的地方都使用
4.3 潜在错误场景
- 推送错误:如果使用错误的 SessionType,消息可能无法正确推送给所有群成员
- 会话混乱:同一个群组可能产生不同的会话ID
- 权限绕过:SuperGroup 会跳过禁言等权限检查
- 安全风险:可以发送链接和二维码,可能被滥用
五、代码修改建议
如果要支持 SuperGroup,建议:
- 创建群组时:允许
GroupType=1和GroupType=2 - 消息发送时:根据
GroupType自动设置SessionType - 消息验证时:明确 SuperGroup 的特殊逻辑,考虑是否需要保留部分安全检查
- 文档说明:明确 SuperGroup 和 WorkingGroup 的差异和使用场景