📹 第十章 GB/T 28181协议原理
📄 一句话概括

国标的安全要求分三层:认证(你是谁)、完整性(没被改)、加密(别人看不懂)。 摘要认证最妙的地方是「密码从头到尾不上线」;但它只保证这一点,不保证内容不被看。

📝 本节和前面几节的关系

上一节讲「多快算合格」,这一节讲「多安全算合格」。 这一节是国标对接里最容易被跳过、也最容易出事的一部分。 很多项目出事不是因为协议不懂,是因为设备还在用出厂口令。

一、认证:密码不上线

SIP 用的是摘要认证,不是明文口令: 平台下发一个随机数,设备拿它和密码一起算哈希,把哈希发回来。 平台那边存着同一个密码,自己算一遍比对,对得上就放行。整条链路上没有一处出现密码明文。 这样一个好处和一个坏处: 好处是抓包抓不到密码;坏处是平台必须存明文密码或它的等价物(HA1),不能只存不可逆摘要。 所以「密码泄露」这件事在国标里等价于「HA1 表泄露」——拿到 HA1 就能冒充设备注册。 这也是为什么 HA1 表要当密码一样保护。 随机数的作用是防重放:每次注册平台都下发新的随机数,同一份报文重发就对不上了。

二、完整性与防重放

摘要认证只保护「认证报文」,不保护「之后的报文」: 注册完之后点播、控制那些报文,抓包能看得一清二楚。 所以标准还要求对信令做完整性保护,常见做法是给每个报文加一个带时间戳的摘要, 或者干脆把整条链路套进 TLS。 媒体加密是可选项,实际用得很少:因为加密要消耗算力,而摄像头的算力很紧。 多数项目靠的是「网络隔离」而不是「媒体加密」。 这里要说一句实话:国标的安全能力是「够用」不是「很强」。 它设计于一个「设备在内网」的年代,放到公网上直接暴露,靠协议本身是挡不住的。

三、工程上真正的三道门

第一道门是网络:摄像机不该直接暴露在公网,该做的端口映射一个都别做,能走专线就走专线。 第二道门是口令:出厂口令必须改,这是所有安全事件里占比最高的一条。 不是「最好改」,是「必须改」。 第三道门是白名单:平台侧限制「哪些 IP 能注册」,这一条能挡掉绝大多数乱扫的尝试。 实现成本几乎为零,效果却比加加密好。 为什么把这三条放在协议之上:因为协议的安全机制要设备配合,而老设备往往不配合。 网络、口令、白名单这三样,不依赖设备实现,落地率最高。

四、动手 Demo:SIP 摘要认证,密码到底有没有上线

用哈希把摘要认证算一遍:HA1、HA2、response 各是什么,报文里发的是哪个。 再算「同一个密码、不同随机数」的三种情况,看防重放怎么起作用。

md5 = lambda s: hashlib.md5(s.encode("utf-8")).hexdigest()
ha1 = md5("%s:%s:%s" % (user, realm, pwd))
ha2 = md5("%s:%s" % (method, uri))
resp = md5("%s:%s:%s:%s:%s:%s"
           % (ha1, nonce, nc, cnonce, qop, ha2))

print('Authorization: Digest username="%s",' % user)
print('  realm="%s", nonce="%s",' % (realm, nonce))
print('  uri="%s",' % uri)
print('  response="%s"' % resp)

for i, nn in enumerate((nonce, nonce, "b91d4e77c0a2"), 1):
    print(i, md5("%s:%s:%08d:%s:%s:%s"
                  % (ha1, nn, i, cnonce, qop, ha2))[:16])

实跑输出:

--------------------------------------------------------------------
SIP 摘要认证,密码到底有没有上线
--------------------------------------------------------------------
**国标要求设备接入必须认证**,用的是 SIP 的摘要认证。**它最妙的地方是:密码从头到尾不上线。**
**这一节用哈希把这个过程算一遍。**

  第一次注册,平台下发随机数 nonce
  量               怎么算                                 前 16 位
  ------------------------------------------------------------------------------
  **HA1**          `MD5(用户:域:密码)`                    `8258341d7dd730ce`
  **HA2**          `MD5(方法:URI)`                        `393e36b0c4c33612`
  **response**     `MD5(HA1:nonce:nc:cnonce:qop:HA2)`     `cf6dab95fe2d1ba5`

**报文里发出去的是 `response`,不是密码**:

  Authorization: Digest username="34020000001320000001",
    realm="3402000000", nonce="a7f3c1e05b2d",
    uri="sip:3402000000@3402000000",
    response="cf6dab95fe2d1ba5ca30e722bd4549c8"

**平台那边存着同一个密码,自己算一遍 `response` 比对**,**对得上就放行。**
**整条链路上没有一处出现密码明文。**

**nonce 的作用是防重放**:

  次序      nonce              nc             response 前 16 位
  ------------------------------------------------------------------
  **1**     `a7f3c1e05b2d`     `00000001`     `cf6dab95fe2d1ba5`
  **2**     `a7f3c1e05b2d`     `00000002`     `fe40ab3340e09a8f`
  **3**     `b91d4e77c0a2`     `00000003`     `9236a138d325176a`

**第一行和第二行 nonce 相同、结果不同**,**因为 `nc` 递增了**;**第三行换了 nonce,结果也变。**
**攻击者截获第一条报文重发,`nc` 还是 1,平台一看「用过了」就丢。**

**但摘要认证只保证「密码不上线」,不保证「内容不被看」**:
**报文本身还是明文,抓包能看见设备编号、指令、甚至 SDP 里的地址。**
**所以标准还要求对信令做完整性保护和加密**,
**实际工程里常见的是「内网用摘要、跨公网套 VPN 或 TLS」。**

**最后量一下这件事的成本**:
  HA1 算一次平均 1.68 微秒(20000 次实测)
  一次注册要算三次,合计 5.03 微秒

**几微秒,完全可以忽略。**
**注意这里说的是「本机 Python 实现」**——**换台设备、换个语言,这个数会差好几倍**,
**但量级都在「微秒」这一档,不会成为瓶颈。**

报文里发出去的只有 response 那 32 个十六进制字符, username 是公开的、nonce 是平台给的、uri 是请求目标,密码一个字节都没有出现。 这三行哈希的分工要记住:HA1 把「用户+域+密码」压成一个值, HA2 把「方法+URI」压成一个值,response 再把两者和随机数混在一起。 第一行和第二行随机数相同、结果不同, 因为 nc 递增了——这是「同一个随机数下不能重放同一条报文」的机制。 第三行换了随机数,结果也变。 攻击者截获第一条重发,nc 还是 1,平台一看用过了就丢。 所以防重放靠的是两个东西一起:随机数换新、计数递增。 最后那个耗时是实测的,一次哈希一点几微秒,一次注册算三次也就五微秒,完全可以忽略。 但注意这是本机 Python 实现的数,换到嵌入式设备上会慢很多,量级仍在一毫秒以内。

30 秒实验

  1. 算一下:一次注册要算几次哈希?(三次:HA1、HA2、response)
  2. 想一下:为什么平台必须存密码或 HA1,不能只存不可逆摘要?
  3. 想一下:摘要认证防不住什么?(报文内容被抓包看见)

常见误区

  1. 以为「摘要认证等于加密」。 它只保证密码不上线,报文内容还是明文。
  2. 以为「设备支持国标就安全了」。 出厂口令不改,协议再规范也没用。
  3. 以为「媒体一定加密了」。 媒体加密是可选项,实际项目大多靠网络隔离。
自测三问(点开对答案) 1. 摘要认证为什么能让密码不上线? —— **因为发的是「密码参与的哈希值」而不是密码本身**,**平台用同一个密码算一遍比对即可。** 2. 防重放靠的是哪两样东西? —— **平台下发的随机数每次换新,加上客户端递增的计数器 `nc`**。 3. 工程上比加密更有效的三道门是什么? —— **网络隔离、改掉出厂口令、平台侧 IP 白名单**。

下一节进流程:第一个流程是注册,它是所有国标交互的起点。

相关笔记