背景
物联网将会成为继互联网之后的下一个暴发点,目前以有不少公司已进入该领域。
智能家居作为物联网中最具有潜力的领域,网络安全是必须引起重视的课题。在互联网领域中,用户去安全性的敏感度并不是那个高。但是在智能家居领域中,如果系统被黑客攻击:半夜房门自动打开、无故解除安防系统、灯不亮等严重威胁到了用户的财产人身安全。
所以,智能家居想发展,安全性是决不可忽视的关键要素。
网络通信安全
任何信息在网络上传播能是能被第三者监听的。
一个局域网就像是一间会议室,里面坐了几个人,大家坐在一起相互交流。同时只允许一个人发言,不管是谁说话都能被在场的所有人听见。
通常情况下,只有被交流的对象才会在意对方说的话,其他人不会留意。
比如:屋子里有A,B,C,D四个人。如下是A与B之间的对话:
A:嘿B
B:嘿A
A:“告诉我银行卡密码吧”
B:“123321”
由于A不是对C与D说的话,C与D自然忽略这句话,但是他们是能听得见的!
如果D是一个不怀好意的人,刻意留心他们的对话,是很容易窃听到关键信息的。
而且网络里还有ARP诈骗等手段,网络安全是岌岌可危的。
怎么办?两种思路:
方法一:不允许不相关的人进入会议室
就是不允许其它结点加入到局域网中来。
在wifi普及面非常广的时代,家家都有无线路由器,这要求用户严守wifi密码,严防泄漏。
这比较难,有时候wifi密码会在用户不知情的情况下泄漏。比如市面上存在一种叫“wifi万能钥匙”,这就是一种盗取用户已登陆wifi结点密码并共享给其它人的工具。这说明这个方法很难把控。
至于如何在这方面有所提升,已超出了我们智能家居所负责的领域。暂不讨论~
方法二:用别人听不懂的语言对话
做不到被听到,那就不让第3个人听懂也行。会议室里有A,B,C 3个人,A与B对英语交流,C在旁听到了,但不知道他们在说什么。
这是我们在安全方面可以做的。
智能家居系统介绍
Server: 云端服务器
SmartHost: 智能主机,负责将以太网中的数据能过Zigbee数据转发给Device
Device: 设备结点,常见的有灯、窗帘、门磁感应器,与SmartHost通过Zigbee连接
Client: 手机应用,有Android客户端与iOS客户端。接受用户的指令。当Client与SmartHost在同一局域网中,Client通过Wifi与SmartHost进行交互;如果不在同一个局域网,则通过与Server进行转发。
假设局域网中来了一个不速之客Hacker,它启图操控家居设备。
攻与防
1. 无加密
如果Client与SmartHost之间的通信是明文的,那么没有安全性可言。只要黑客进入了家里的局域网,对网络包进行监听,很轻易地操控家里的设备。
通信过程
Client-->SmartHost:
Login: account John, password abc
SmartHost-->Client:
OK
Client-->SmartHost:
Open the door
SmartHost-->Client:
Done
攻击方式
Hacker监听到上面的通信,Hacker可以进行模拟Client进行登陆
Hacker-->SmartHost:
Login: account John, password abc
SmartHost-->Hacker:
OK
Hacker-->SmartHost:
Open the door
SmartHost-->Hacker:
Done
于是门开了。
分析
由于是明文,稍有头脑的Hacker就能分析出Client与SmartHost的通信协议。在盗取了用户的帐号与密码之后,Hacker就可以完全掌据整个家居系统。
防预级别:V 只能抵挡没有网络基础的攻击
2. static_key
由于上面问题的是明文数据传输,容易被Hacker分析。于是在上一版本的基础上使用特定的密钥对数据进行加密。
如下用()表示其中的内容是用static_key加密的
Client-->SmartHost:
(Login: account John, password abc)
SmartHost-->Client:
(OK)
Client-->SmartHost:
(Open the door)
SmartHost-->Client:
(Done)
由于上面是用密文进行通信的,Hacker不知道其容易是什么意思。但是,他知道只要把上面的话录制下来,重复播放一便就可以伪装Client开门了。
Hacker-->SmartHost:
(Login: account John, password abc)
Hacker-->SmartHost:
(Open the door)
结果门开了。
分析
其实Hacker不知道Client与SmartHost的具体内容是什么。他可能猜到是在登陆系统,然后是发命令开门的动作。但他无法得知通信的结构,也无法获取用户的帐号与密码。它只能说模仿Client的“发音”与SmartHost进行对话,达到控制设备的目的。
防预级别:VV
3. 加时间戳
针对上面Hacker的攻击,我们做了改进。通常,Hacker不会在监听到了命令之后立即执行。而是在等主人不在家了再执行上面的开门命令。那么,我们在密文域中加一个字段:Timestamp(时间戳)。
通信过程
Client-->SmartHost:
(201509261543, Login: accout John, password abc123)
SmartHost接收到命令,当前系统时间为2015-09-26 15:44,与Client提供的命令误差在5分钟以内,通过检查。
SmartHost-->Client:
(201509261544, OK)
Client接收到命令,当前系统时间为2015-09-26 15:43,与Client提供的命令误差在5分钟以内,通过检查。
Client-->SmartHost:
(201509261548, Open the door)
SmartHost接收到命令,当前系统时间为2015-09-26 15:49,与Client提供的命令误差在5分钟以内,通过检查。
SmartHost-->Client:
(201509261549, OK)
Client接收到命令,当前系统时间为2015-09-26 15:48,与Client提供的命令误差在5分钟以内,通过检查。
攻击过程
Hacker监听到了上面的整个过程,在主人离开了家,在21:09实施攻击。
Hacker-->SmartHost:
(201509261543, Login: accout John, password abc123)
SmartHost接收到命令,当前系统时间为2015-09-26 21:09,与Hacker提供的命令误差超出5分钟,丢弃。
攻击失败。
分析
由于Hacker不知道static_key,不知道Client-->Server的明文是什么。所以他无法提取其中的帐号与密码信息。同时,他也没有办法更改密文中的timestamp。所以,无法成功突破。
防御等级:VVV
通抵御普通的Hacker攻击在时间间隔较长的指令伪装攻击。
5. 加序列号
上面的方式能抵挡超过5分钟的命令伪装,但是不能解决5分钟内的命令伪装问题。于是,我们在上一版的基础上,在指令中加添了Serial域,即序列号,用于防止短时间内的重复指令。
在通信中,我们会随机生成一个Serial:xxxxxxx,接收方收到指令后,会去查 SerialTable看5分钟内有没有xxxxxxxxx存在。
如果没有找到,则处理该命令,然后将xxxxxxx加入到SerialTable中。 如果找到,则丢弃该指令。
由于系统只保存5分钟的Serial,不会有太大的查表负担。
如此与时间戳结合使用,便可以彻底解决Hacker伪装命令的问题。
分析
与timestamp相似,由于Hacker无法获得明文,所以他没有办法修改里面的timestamp与serial。将已发送过的数据包再发一次,必然导致失败。
6. static_key泄密灾难
上面的安全措施看起来比较完善了。但是,它有3个前提条件:
加密算法过于强硬,无法逆运算
暴力破解代价大
第三方无法获得通信的加密算法与密钥
对于问题1,我们可能采用国际上公认的加密算法进行加密,如openssl中的Aes。
对于问题2,我们可以使得加密的密钥过于复杂,比如128字节的密钥进行加密。如果使用暴力破解,其代价太大。
问题3是最大的问题。
有句话叫作:“日防夜防,家贼难防”,由于用于加密的密钥是写在代码里的。开发工程师是再清楚不过了,加密的密钥非常容易泄漏。
一旦static_key泄密,那么所有的加密如同没有加密!Hacker所向披靡如入无人之地
解决思路一:
不允许普通的开发工程师涉及加密算法与加密密钥。将加密算法的设计实现工作交给公司核心人士去实现。其它普通的研发工程师只能拿到一个二进制的静态库或动态库。
但这也有一点需要注意的,不管是生成什么样的加密过程,加密的密钥必须不能是明文。比如:
const char *encrypt_key = "adi3dfa;9era"; ...
因为生成的二进制文件里有保存明文,可以轻易地用工具搜出来。如:
strings libEncrypt.a
就会列出库中的所有明文。好奇心强的工程师很容易地就从中找出你的加密密钥了。
就算工程师没有泄密,Hacker自己去买台SmartHost,对程序用strings
分析,也是能很轻松找出static_key的。
必须是动态生成的。如:
//! 这个名字别启得太明显,要大众化点 char encrypt_key[128] = {0}; //! 这个名字别启得太明显,要大众化点 void GenerateEncryptKey() { //! 在这里通过某种算法生成encrypt_key中的密钥 }
这样,不能轻而易举地获取到。但是,这也不是说获取不到了,只要单步跟踪调试,也能获取,只是麻烦了点。
解决思路二:
信条:不能将加密算法设计者自己挡在外的加密算法不是好算法!
摒弃static_key的加密方法,每个产品在出厂时都有一个特有密钥unique_key,并且在服务器的数据有表。
以下以[]表示被unique_key加密的数据
SmartHost登陆Server
用户购买了SmartHost后,SmartHost首次启动,登陆Server。
SmartHost-->Server:
(uid=xxxxxxxx)[timestamp=201509291255, serial=82342, I am SmartHost, model=M1, Log in]
注意:SmartHost在介绍自己uid时是用static_key加密,之后使用unique_key加密。Server-->SmartHost:
[Allow]
Server从数据包中提取了uid,从数据库中查找到对应uid的unique_key,用unique_key加密回复的数据。之后的数据包都是用SmartHost的unique_key进行加密。
如此,只有数据库的super administor有权限得知对应uid SmarHost的unique_key。
Client与SmartHost
用户要使用Client绑定SmartHost,但Client本身不知道SmartHost的uid,也不知道SmartHost的unique_key。如何与SmartHost通信?
在 SmartHost 的背后有一个二维码,就是该SmartHost的unique_key。用户用Client上的二维码扫扫功能,获取该SmartHost的unique_key。
通信过程:
搜索SmartHost
Client用UDP广播数据包:
[timestamp=201509291255, serial=24234, tell me your IP address]
SmartHost收到数据包后,用unique_key进行解密,验证timestamp域,发现结果正常 ,表示解密成功,证明该数据包正是发给自己的。于是回复UDP包:
[timestamp=201509291255, serial=3234, ip_address=192.168.x.x]
Client收到UDP数据包,用unique_key进行解密,得到了SmartHost的IP地址。
如果同时出现2个以上的SmartHost,只有使用了对应unique_key的SmartHost才能通过timestamp验证(错误的unique_key解密所得的timestamp是没法通过时间戳验证的)。
绑定SmartHost
后面的与主机交互过程都使用unique_key进行加密。
Client与Server
用password的md5sum当作unique_key进行通信,默认带timestamp与serial验证。
注册帐号
Client-->Server: (Regist: account=Lucy, password=98789) Server-->Client: (Done)
服务器在接收到该注册信息后,会将password的md5sum作为unique_key与client进行数据加密。以下以[]表示用unique_key加密的数据。
在注册时,加密等级是很低的。
登陆
Client-->Server: (Login: account=Lucy)[timestamp=201509292100] 告诉Server自己的account,然后Server根据account到数据库查
表,获得unique_key,并用其解密timestamp,再进行时间域校验。通过则表示登陆成功,否则表示失败。
Server-->Client: [timestamp=201509292100, OK]
模拟破解
对于不了解通信协议的Hacker,即使使用包伪装与重发包的方式已经是束手无策了。
这里的Hacker,是指协议的设计者或对协议非常熟悉的人,假设他们:
已获得static_key与加密解密算法
不能接触到服务器与机密数据
不能接触到用户手上的SmartHost
行动:
假设Hacker已通过某种方法加入了SmartHost的局域网。
通过监听器,捕获到了SmartHost的登陆包,得到SmartHost的uid。由于没有unique_key,得不到SmartHost与服务器的通信内容。但他知道密文解密后的明文内容,可能使用暴力破解法进行破解。但是时间成本太高。
如果监听到Client的注册过程就可以,因为注册数据包是用static_key加密的,可以破解。Hacker可以获得用户的密码,进而算出md5sum的unique_key的。用这个来伪装真正的Client,对SmartHost进行操控。
此方法的关键点在于要抓到注册数据包,一个用户只有一次,而且要在与Hacker所在局域网中进行才有可能被抓到。如果打算对SmartHost进行攻击时,用户早都已经注册了帐户。诱使用户使用盗号码木马App,从Client的手机中窃取Client保存在手机系统中的文件。从而获取Client的帐号密码,以及SmartHost的unique_key。
从上可得出,通过监听方式来破解似乎只有暴力破解法了。而更可行的破解方案还是盗号木马。
因为我们总得在手机上保存主机的unique_key与用户的帐号与密码。否则就得每次让用户去扫SmartHost背后的二维码,每次都让用户重新输入帐号密码登陆。用户肯定受不了。
关于上面的安全性问题该如何解决,博主还没有想到合理的解决方案,欢迎讨论。上面的讨论如有纰漏,欢迎指正。
博主从事智能家居领域研发,欢迎关注!