5.6 序列号负载 (Sequence Number Payload)
序列号(SEQ)负载用于提供重放攻击保护。它在GROUPKEY-PULL响应和GROUPKEY-PUSH消息中使用。
SEQ负载格式
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Next Payload ! RESERVED ! Payload Length !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Sequence Number !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
字段说明:
- Next Payload: 下一个负载的类型
- Payload Length: 固定为8字节
- Sequence Number: 32位序列号
序列号的使用
在GROUPKEY-PULL中
GCKS可以 (MAY) 在GROUPKEY-PULL响应中包含SEQ负载:
HDR*, HASH(2), SA, [KE,] [NONCE,] [SEQ]
SEQ负载的值:
- 初始值: GCKS为该群组当前使用的序列号
- 用途: 告知组成员下一个GROUPKEY-PUSH消息将使用的序列号范围
在GROUPKEY-PUSH中
GCKS必须 (MUST) 在每个GROUPKEY-PUSH消息中包含SEQ负载:
HDR*, SEQ, [SA,] [KD,] [POP,] [D]
SEQ负载的值:
- 递增: 每个新的GROUPKEY-PUSH消息的序列号必须 (MUST) 大于前一个
- 唯一性: 每个GROUPKEY-PUSH消息有唯一的序列号
序列号的初始化
GCKS在开始向群组发送GROUPKEY-PUSH消息时:
- 选择初始值: 选择一个初始序列号(可以是0或随机值)
- 通知成员: 通过GROUPKEY-PULL响应中的SEQ负载告知成员
- 递增: 每发送一个GROUPKEY-PUSH消息,序列号加1
重放检测
组成员的处理
组成员必须 (MUST) 实现重放检测:
- 记录序列号: 记录最后接收和处理的GROUPKEY-PUSH消息的序列号
- 比较序列号: 接收新消息时,比较其序列号与记录的值
- 拒绝旧消息: 如果新消息的序列号小于或等于记录的值,拒绝该消息
- 处理新消息: 如果新消息的序列号更大,处理该消息并更新记录的序列号
处理序列号间隙
如果组成员检测到序列号间隙(如从5跳到8):
- 可能原因: 数据包丢失或乱序
- 处理方式:
- 继续处理序列号更大的消息
- 可以 (MAY) 请求GCKS重传丢失的消息(如果协议支持)
- 可以 (MAY) 执行新的GROUPKEY-PULL以获取最新状态
序列号回绕
序列号是32位的,最终会回绕:
- 回绕检测: 实现应该 (SHOULD) 检测序列号回绕
- 处理方式: 当序列号从大值跳到小值时,如果跳跃足够大(如从0xFFFFFFFF到0x00000001),视为回绕而非重放
- 重新初始化: GCKS可以 (MAY) 在回绕时重新初始化群组
与ISAKMP消息ID的关系
SEQ负载与ISAKMP头中的消息ID(Message ID)协同工作:
- 消息ID: GCKS为每个GROUPKEY-PUSH生成唯一的消息ID
- SEQ负载: 提供额外的序列保证
- 双重保护: 两者结合提供更强的重放保护
处理规则
GCKS规则
GCKS必须 (MUST):
- 初始化序列号: 为每个群组维护一个序列号计数器
- 递增序列号: 每发送一个GROUPKEY-PUSH消息时递增序列号
- 包含SEQ负载: 在每个GROUPKEY-PUSH消息中包含SEQ负载
- 持久化: 将序列号持久化存储以防止重启后重用
- 通知初始值: 在GROUPKEY-PULL响应中可选地通知初始序列号
组成员规则
组成员必须 (MUST):
- 初始化状态: 从GROUPKEY-PULL响应中的SEQ负载(如果有)初始化期望的序列号
- 验证序列号: 对每个接收的GROUPKEY-PUSH消息验证序列号
- 拒绝重放: 拒绝序列号小于或等于已处理消息的消息
- 更新状态: 成功处理消息后更新记录的序列号
- 处理间隙: 正确处理序列号间隙和回绕
安全考虑
序列号提供重放攻击保护:
- 防止重放: 防止攻击者重放旧的GROUPKEY-PUSH消息
- 必需保护: SEQ负载必须 (MUST) 受到完整性保护(通过KEK)
- 不可预测: 初始序列号可以 (MAY) 使用随机值以增加安全性
- 持久化: GCKS必须 (MUST) 确保重启后不重用序列号
没有序列号保护,攻击者可以:
- 重放旧的GROUPKEY-PUSH消息
- 导致组成员使用过期的密钥
- 破坏群组的前向访问控制
因此,正确实现序列号机制对GDOI的安全性至关重要。
示例
示例1: 正常操作
GCKS发送:
GROUPKEY-PUSH #1, SEQ = 100
GROUPKEY-PUSH #2, SEQ = 101
GROUPKEY-PUSH #3, SEQ = 102
组成员接收:
接收SEQ=100, 接受(首次)
接收SEQ=101, 接受(101 > 100)
接收SEQ=102, 接受(102 > 101)
示例2: 重放攻击
GCKS发送:
GROUPKEY-PUSH #1, SEQ = 100
GROUPKEY-PUSH #2, SEQ = 101
攻击者重放:
重放GROUPKEY-PUSH #1, SEQ = 100
组成员处理:
接收SEQ=100, 接受(首次)
接收SEQ=101, 接受(101 > 100)
接收SEQ=100, 拒绝(100 <= 101, 重放攻击)
示例3: 数据包丢失
GCKS发送:
GROUPKEY-PUSH #1, SEQ = 100
GROUPKEY-PUSH #2, SEQ = 101 (丢失)
GROUPKEY-PUSH #3, SEQ = 102
组成员接收:
接收SEQ=100, 接受
接收SEQ=102, 接受(检测到间隙,但继续处理)
(可选)请求重传SEQ=101