绑定,是为了让用户和设备之间建立联系,从而只有这个用户有权限对设备进行控制,使用。
鉴权,是为了验证设备的真实性。鉴权实际上主要是云端对设备端进行鉴权,避免有人冒充设备,发送假数据给云端,从而让用户把假设备当作真设备使用。
为什么要将绑定和鉴权放在一起说呢,因为我觉得这两个功能实际上是相互关联的。实际上设备与云平台的任意一次交互,云平台都需要直接或者间接的验证下面的几条内容:
1.与我通信的设备是不是我们生产的设备
2.与我通信的设备是不是用户合法绑定的设备
3.我收到的数据有没有被修改过
其实在做流程的时候,我们要一直做这样的假设:我们防范的不是其他人,而是我们自己的工程师。如果有一天我们的工程师离职了,他们会怎么做。如果我们的工程师带走的关键数据,他们会怎么使用。
一.是不是我们生产的设备
这一步通常在绑定的时候会进行验证。如果设备是被clone或山寨出来的,云平台是不会允许他们与用户进行绑定的。
IOT在设备生产的过程中,总是需要烧写一些内容在flash中,来让设备证明自己的唯一与真实。通常情况下,云平台会为设备分配“设备ID”与“设备密钥”。
设备ID代表了设备的唯一性,通常会使用MAC地址,或一些唯一的字符串(MODEL+比如MAC地址经过md5签名后=MODEL+12345678)。无论使用哪种方式,设备ID在整个云平台内,都是唯一的。云平台通常有一个专门的数据库,用来存放已经生产的设备ID。只要提供设备ID,通常云平台就可以查询到这个设备的所有信息。
设备密钥代表了设备的真实性。设备密钥就像设备的指纹,只有设备自己会随身携带,云端通常也会有一份对应的密钥。设备在需要的时候,可以用指纹进行解锁后,和云端进行通信。
这里我提到云端有一份对应的密钥,而不是与设备密钥相同的备份。这其实与设备的密钥使用方法有关。通常情况下,设备密钥有以下几种使用方式:
1.设备端的密钥与云端的密钥相同。这个是我们最容易理解的,毕竟双方有着相同的钥匙,无论是加密解密,还是进行带密钥的签名,都是可以很容易进行相互确认的。缺点就是这个密钥有可能由于被云端泄漏,而造成大面积的损失。因为云端通常需要保存一份设备密钥与设备ID的对应关系,因此这个数据库实际上需要很强的保密性,一般人不能随意接触到。同时这种方案需要设计特殊的生产流程,在生产的过程中,设备ID与设备KEY的对应关系,理论上是不可以被明文存储或保存的。
2.设备端存储私钥,云端存储公钥。这样的方案的好处是,云端即使泄漏了公钥,设备的真实性依然无法被复制,因为设备的私钥只有这台设备携带,任何地方都没有备份。在生产的过程中,通常需要在设备内生成私钥与公钥对,同时将公钥导出。私钥需要尽可能少的走动,最好仅在且永远在这台设备内。
3.设备端存储公钥,云端存储公钥私钥。目前见过有厂家使用这样的方法,但是为何这样使用,个人感觉可能和他们工厂公钥私钥的生成方法有关。而且云端同时存储公钥私钥的做法(可能也存储了设备ID),增加了云端数据泄漏时的影响范围。
这里不得不强调一下公钥私钥的使用方法。虽然公钥私钥可以相互配合进行加密解密,但是更重要的一点是,在使用过程中,通常公钥都用来加密密和验证签名,而私钥用来解密和生成签名。也就是说,私钥是私钥持有者身份的证明,所以与我通信的人,都需要用我的公钥进行加密,因为只有我有私钥可以解密;我可以用我的私钥对摘要做签名,因为有所持有我公钥的人,都可以通过公钥验证我的签名。
如果想要保证数据的机密性,我们常见的做法是,通信双方通过非对称加密安全交换对称加密的密钥,后续通信过程的数据都使用对称加密保证数据机密性。
当使用对称密钥方案的时候,设备端可以将一个加了salt的字符串,使用密钥进行hmac签名。云端拿到这个salt后,同样进行hmac签名后进行对比。
如果使用非对称密钥方案,设备端也可以将一个加了salt的字符串,使用私钥进行RSA签名(JWT)。云端拿到这个明文与签名后,使用公钥进行签名验证。
需要注意的是,无论使用加密还是非加密,验证设备密钥的方法都是签名,而不是加密。因为签名是不可逆的,无法通过签名获取明文内容。
我们需要遵循一种最简单的密钥生成原则:
密钥的存储是不可靠的,我们要时刻认为,设备端的密钥是可以被轻易读出来的。
这两个原则也就引出了接下来的问题,如果设备端的密钥被轻易获取了,我们如何保证设备的安全呢?
二.是不是用户合法绑定的设备
这一节的内容用来解释上一节中最后的问题。如果设备端的密钥被轻易获取了,我们能不能保证用户的设备不受影响呢?答案是不能。
很简单,设备端的密钥被获取了,也就代表着这个设备可以被clone出来,可以做到与真正的设备一模一样,或者说这就是一个真实的设备,可以做一切真实设备可以做的事情,除了还原与原用户进行绑定关系。
设备在使用前通常会由用户进行绑定操作。这个操作通常需要一些物理操作,或者需要用户与设备的距离非常近,才可以进行。因此,这样的绑定操作通常是无法被轻易破解与重放的。
我们在最初讨论绑定原则的时候,提出了如下建议,这些建议也称为后期指导我们所有鉴权的基础:
1.绑定后需要生成一个token,这个token需要由云端与设备端共同生成,且整个生成过程中需要保证流程的加密。
2.token的生成,需要有设备端key的参与,并由云端对token“加盐”(由云端增加随机性)。token的生成不可逆,不要直接使用加密解密。例如token=MD5(设备密钥+云端随机数)。
3.生成的token是绑定后云端验证设备合法性验证的唯一标准。token不可直接被用来传输。
这样一来,我们将用户的绑定动作(不可重现不可逆),作为一个参数加入到鉴权的过程中。对于绑定后设备的鉴权,不仅需要对设备的key鉴权,更需要对绑定关系进行鉴权。设备的key存储在flash中不会改变,绑定关系却会随着绑定发生变化。
为什么强调对绑定关系进行鉴权呢?假如我购买了一台设备,读取设备key后再退货,实际上我是可以完全clone这样一台设备来的。仅clone设备的用途并不大,但是如果我能用这台加设备的信息,连接到真实的设备上(例如获取音视频传输的权限),才是真的可怕。
实际上我们在考虑绑定鉴权的过程中,本着这样的一个原则:我们防范的不是仅仅外面的黑客,更是我们本身的开发人员。如果由开发人员离职了,对我们设备的安全性会不会存在威胁?
三.这个设备的通信数据有没有问题
前面两个问题都是为了这个问题做铺垫。我们给设备颁发“身份证”,给设备与用户颁发“结婚证”,实际上都是为了一个目的:让云端知道正在和自己通信的设备,是不是用户正在使用的那台设备。
IOT设备的通信通常氛围两种,一种是MQTT/AWSIOT等基于长链接的通信,另一种则是http/https等云端请求。MQTT/AWSIOT等一套自己的鉴权方式,通常是设备端持有公钥/私钥,用于与云端进行双向认证。这里不做详述。对于http等通信方案,我们应该怎样设计呢?
有了前两个问题的铺垫,我们自然而然的得出了下面的方案:
1.不使用http,必须使用https,保证数据报文的加密性。
2.对http的body进行签名,签名要放到header中供云端进行验证。这样保证了签名无法用于其他的报文。
3.http的body中最好带有时间戳,这样云端可以通过验证时间戳防止重放
4.最好对http的body进行加密。毕竟通过代理,可以很容易的截取到https的报文内容,尽管https本身存在加密。
实现了上述四点,基本上可以在保证报文安全的情况下,让云端对报文的发送者进行鉴权,确保了报文的数据是合法的,唯一的。
云端如何验证设备真实性
云端验证设备的真实性,实际上验证的是设备端存储的那个密钥。无论是使用相同的密钥,还是使用公钥私钥的形式,我们都希望能够在不暴露密钥本身的情况下,让云端对密钥进行验证。
那么,我们应该如何来验证一个密钥呢?
方案一:把密钥通过https传输给云端。实际上无论是放在https的header中,还是放在body中,这种传输方法都是错误的。攻击者实际上只要通过一个代理,就很容易解析出https中的内容。特别是在有的开发者故意不使用https的情况下。
方案二:通过加密的方法,将加密后的字符串作为token传输给云端进行验证过。这样做确实比方案一隐蔽了一些,但是需要注意的是,加密是一种可逆的操作。也就是说攻击者一旦掌握了你加密的方法,就有可能通过收集大量http请求的报文内容,通过暴力破解的方式,将你的key试出来。
方案三:通过签名的方法,使用key作为密钥,对一些内容进行签名。签名后的内容是不可逆的,因此破解的难度相对方案二来说更大一些。无论是使用私钥签名,还是使用hmac签名,安全性相较前两个方案都更高一些。
通过上面两个方面的描述,我们可以清楚的知道,验证设备的合法性,实际上验证的就是设备所包含的一个密钥。无论这个密钥是存储在flash中,还是每次启动生成在缓存中,亦或是存储在安全加密的flash中,这个密钥最终都会被云端进行验证。
四.还有什么其他的方案?
在工作中和友商们接触的多了,通常也可以看到很多各种各样的方案,这里挑一些有意思的和大家分享下:
1.设备存公钥,云端存私钥。
这个方案实际上是可以用的,但是友商的小伙伴似乎搞混了公钥私钥的用法。在使用的过程中,公钥和私钥仅用于加密和解密。实际上,公钥和私钥之所以不同,原因是他们一个用于加密,一个用于签名。可能是为了避免设备端和云端存储相同的密钥吧,但是这样公钥和私钥的用法,实际上也并不规范。另外,破解公钥的难度要比私钥小的多,设备端存储公钥,可能仅仅是为了保证端云持有不同的密钥吧。
2.绑定关系token可以更换。
绑定时生成的token可以更换,这个本身没有什么问题,但是需要注意的是更换的过程。但后来发现友商的小伙伴在更换的过程中,仅使用设备端密钥即可对设备端持有的token进行更换,一旦设备的key被泄漏,这个设备变成了一台可以随时被控制的设备,无论怎样操作,都将是一台不再安全的设备了。
3.设备端存储IOT证书。
设备端存储密钥是正常的,但是设备端存储IOT证书,实际上是十分危险的。在生产的时候倒入IOT证书,也意味这这个IOT证书无法被更换。一旦IOT证书被泄漏,这个设备的IOT通道就可以随时被进行访问。通常我们的建议是,IOT证书应在设备绑定后,从云端获取,且设备每次获取的IOT证书都需要被重新颁布,不可以一直不变。
4.只要我们的开发人员不泄漏设备的关键信息,设备就是安全的。
所谓防御往往是从内部被攻克的,如果逻辑本身存在漏洞,我们不能把希望寄托于开发人员自身的职业素质。况且想要获取设备的逻辑,也有这各种个样的方法。我们要做的,就是将设备的逻辑完善起来(注意不是复杂,是完善),将开发人员本身作为防范的对象,来构建我们的安全逻辑。如果固件开发工程师离职了,我们的设备是否安全?如果固件开发工程师和云端开发工程师一起离职了,我们的设备是否安全?