4. 服务与特征的 UUID 规划
蓝牙GATT的安全,说白了就是解决三个问题:你是谁、数据有没有被偷看、消息有没有被篡改。很多开发者把注意力全放在服务设计和UUID分配上,结果产品一上线就被抓包工具秒破——嗯,这种事我见过不止一次。
这一章我们就把GATT安全机制的底裤扒干净。从配对绑定到加密认证,再到那些让人头大的安全等级和权限位,我会把每个坑都指给你看。
4.1 配对与绑定:建立信任的第一步
配对(Pairing)和绑定(Bonding)是两个容易混淆的概念。我习惯这么记:配对是过程,绑定是结果。
- 配对:两个蓝牙设备临时交换身份信息,建立加密连接的过程。
- 绑定:配对完成后,双方把对方的身份信息(Long Term Key,简称LTK)存下来。下次连接直接复用,不用再走一遍配对流程。
配对流程分三个阶段:
- 配对特征交换:双方互相告诉对方自己支持什么IO能力、有没有OOB数据、要不要绑定。
- 短期密钥(STK)生成:根据双方能力协商出一个临时密钥,用来加密当前连接。
- 传输特定密钥分发:把LTK、IRK、CSRK等长期密钥分发给对方。这一步只有绑定模式下才会做。
关键点:如果只配对不绑定,每次断开重连都要重新走一遍配对流程。这在耳机、手环这类频繁重连的设备上体验极差。所以消费类产品几乎都做绑定。
我在项目中遇到过一个问题:某款智能锁和手机配对后,手机换了APP再连就提示“设备已被绑定”。查了半天才发现是绑定信息没做清除接口——用户换手机就废了。所以绑定信息的管理接口一定要留,别做死。
4.2 加密与认证:数据安全的双保险
加密(Encryption)和认证(Authentication)是两回事,但经常被混为一谈。
- 加密:保证数据在传输过程中不被偷看。用的是AES-CCM加密算法。
- 认证:保证通信的对方确实是它声称的那个设备。防止中间人攻击(MITM)。
蓝牙4.0到4.1用的是传统安全配对(Legacy Pairing),加密强度一般,而且容易被暴力破解。从蓝牙4.2开始引入了安全连接(Secure Connections,SC),使用ECDH密钥交换和AES-128-CTR加密,安全性上了一个大台阶。
| 特性 | Legacy Pairing | Secure Connections |
|---|---|---|
| 密钥交换算法 | 基于临时随机数 | ECDH(椭圆曲线Diffie-Hellman) |
| 加密算法 | AES-CCM 128位 | AES-CCM 128位 |
| MITM防护 | 可选,强度低 | 强制支持,强度高 |
| 密钥长度 | 固定128位 | 128位,但熵更高 |
| 兼容性 | 蓝牙4.0/4.1设备 | 蓝牙4.2+设备 |
注意:如果你的产品需要兼容蓝牙4.1及以下的旧设备,必须同时支持Legacy和SC两种模式。但建议默认优先使用SC,只有在对方不支持时才回退到Legacy。
4.3 安全等级:MITM与LESC
蓝牙规范定义了四种安全等级,从低到高分别是:
- 等级1:无安全(No Security)——不加密,不认证。只适合传输公开数据,比如温度传感器广播。
- 等级2:仅加密(Encryption Only)——数据加密传输,但不验证对方身份。能防窃听,防不了中间人。
- 等级3:加密+认证(Encryption + Authentication)——加密且验证身份。需要MITM保护。
- 等级4:加密+认证+LESC——在等级3基础上,强制使用Secure Connections和128位密钥。这是目前最高的安全等级。
这里重点说一下MITM(中间人攻击)和LESC(低功耗安全连接)。
MITM保护
MITM攻击的场景是这样的:攻击者假装是设备A跟设备B通信,同时又假装是设备B跟设备A通信。两边都被蒙在鼓里。
防止MITM的关键在于带外确认。比如手机和手环配对时,两边都显示一个6位数字,你确认一下数字是否一致——这就是MITM保护。如果攻击者插在中间,两边的数字对不上,配对就会失败。
我曾经帮一个客户排查问题:他们的智能门锁老是配对失败。后来发现是IO能力配置错了——门锁只有LED显示,没有输入按键,但代码里配成了“KeyboardOnly”。结果手机端要求用户输入数字,门锁端却显示不了,两边永远对不上。嗯,IO能力一定要如实配置,别为了省事瞎填。
LESC(低功耗安全连接)
LESC是蓝牙4.2引入的增强安全机制。它使用ECDH密钥交换,生成的密钥强度远高于Legacy模式。而且LESC强制要求MITM保护,不能跳过。
LESC的配对确认方式有两种:
- Numeric Comparison:两边显示一个6位数字,用户确认一致。适合双方都有显示屏和确认按钮的设备。
- Passkey Entry:一方显示数字,另一方输入。适合一方有屏、一方有键盘的场景。
- Out of Band(OOB):通过NFC、二维码等外部通道交换配对信息。安全性最高,但实现也最复杂。
我的建议:如果产品面向消费市场,优先用Numeric Comparison。用户操作简单,安全性也够。Passkey Entry在智能家居场景里很常见——手机输入门锁上显示的数字。OOB虽然安全,但需要额外硬件支持,成本高。
4.4 ATT权限与属性权限
安全机制最终要落地到具体的属性(Attribute)上。GATT协议里,每个属性都有权限位,用来控制谁能读、谁能写、什么条件下能操作。
属性权限分三类:
- 访问权限(Access Permissions):读、写、读写、不可访问。
- 加密权限(Encryption Permissions):是否需要加密连接才能访问。
- 认证权限(Authentication Permissions):是否需要配对认证才能访问。
- 授权权限(Authorization Permissions):是否需要应用层额外授权(比如用户点击确认)。
这些权限可以组合使用。比如一个心率测量特征值,可以配置为:
// 伪代码示例:心率特征值权限配置
Characteristic heartRateChar;
heartRateChar.uuid = 0x2A37;
heartRateChar.properties = READ | NOTIFY;
heartRateChar.permissions = {
.read_encrypted = true, // 读操作需要加密
.read_authenticated = true, // 读操作需要认证
.read_authorized = false, // 不需要额外授权
.write_encrypted = false, // 不允许写
.write_authenticated = false,
.write_authorized = false
};
这个配置的意思是:只有经过配对认证且加密连接的设备才能读取心率数据。未配对的设备连读都读不了。
踩坑提醒:我曾经见过一个产品,开发者把“需要加密”和“需要认证”搞混了。结果设备只配了对没绑定,每次重连都要重新配对,用户体验极差。记住:加密是加密,认证是认证,绑定是绑定。三个概念别混用。
4.5 安全机制选择决策树
说了这么多,到底该怎么选?我画了一张图帮你理清思路。
4.6 实战建议:安全配置清单
最后,我整理了一份安全配置清单。每次做GATT服务设计时,对着这张表过一遍,基本不会漏。
| 检查项 | 说明 | 常见错误 |
|---|---|---|
| IO能力配置 | 如实填写设备的输入输出能力 | 为了省事填NoInputNoOutput,导致无法做MITM保护 |
| 绑定标志位 | 是否需要存储LTK实现自动重连 | 忘记设置Bonding标志,每次重连都要配对 |
| 属性权限 | 每个特征值的读写权限是否合理 | 敏感数据没设加密权限,裸奔在空气中 |
| 安全等级选择 | 根据数据敏感度选择1-4级 | 所有服务都用最高等级,导致配对慢、功耗高 |
| 密钥存储 | LTK、IRK等密钥是否安全存储 | 密钥明文存在Flash里,被读出来就废了 |
| 兼容性测试 | 是否兼容不同蓝牙版本的主机 | 只测了iOS没测Android,或者反过来 |
一个小技巧:开发阶段可以把安全等级降到最低,方便抓包调试。但发布前一定要改回来。我曾经见过一个产品,发布固件里忘了改安全配置,结果用户数据全程明文传输——嗯,那个版本被紧急召回重刷了。
GATT安全机制就讲到这里。记住一句话:安全不是功能,是属性。它应该渗透到每个服务、每个特征值、每个属性的设计中去,而不是最后才想起来“哦,我们好像还没做安全”。