浅谈IM软件如何建立安全socket连接、登录

----------------------------------------------------欢迎查看IM软件业务知识《专栏》-------------------------------------------------------------------
使用状态机来保持在线状态 【点击】                      拼图算法,将零碎小图,整理到一张大图上【点击
登录导航 【点击】                                                   会话session的概念【点击】       
“假在线’ 【点击】                                                    非对称加密,RSA算法【点击
如何建立安全socket连接、登录 【点击】               浅谈断线重连、心跳和长在线 【点击
iOS 客户端创建网络连接,常见错误汇总 【点击】 protocolBuf 在iOS上的使用入门、讲解、指南 【点击
实现富文本解析【点击】                                          UITableview Deceleration 加速滑动(惯性滑动)、弹性回归原理【点击
--------------------------------------------------------------------------------------------------------------------------------------------------------------------

一、概述

    今年3月份,在QQ发布带有“支付功能和设备锁”的版本的时候,当时就想写一下IM软件安全登录相关的博客。如今IM软件已经逐步称为一个平台,他的安全性不可马虎!今天有时间,写一段IM登录的文章吧

        登录承接者建立安全连接的重任。提到登录,作为IM软件第一步可谓首当其冲,用户密码在此处输入,安全意义不言而喻。登录信是最复杂信令之一,可以肯定的说,登录信令的应答码是最多的,不仅仅有200错误码,400错误码,500的应答码,甚至达到20种应答,不同错误码代码不同结果,每个都需要处理。

二、如何建立安全socket连接

        首先是网络连接,我们跳过去。本博文前提网络已经连上,客户端准备发起身份认证,我把安全策略形容成是一层层的,最基本的一层就是对明文密码的保护。

第一层、保护密码

对密码进行,sha1或者MD5,进行信息摘要,避免传递明文。这种称为“摘要认证”!下图是MD5加密前后对比,MD5的特点是,无论多长的输入,输出固定长度且唯一的字符串。


“信息摘要”做到了下面几点:
永远不会以明文方式在网络上发送密码。(如果本地有存储,也是摘要信息,不要存储明文)
● 可以防止恶意用户捕获并重放认证的握手过程。
● 可以有选择地防止对报文内容的篡改。

第二层、防止重放

     使用摘要就无需用明文发送密码了,仅仅隐藏密码不能避免危险,即便不知道密码,黑客也可以截获摘要,再次发给Server,伪装登录。可是使用【时间戳+随机数】来防止重复。
     这种方法简单,不需要服务器向客户端发送随机数,而是依赖时间戳。Server可以控制时间窗口,防止重放。
     为什么还要用“时间戳”?
     因为:Server不可能记住所有的随机数,比如说一年前的,所以需要一个时间来限制,server只记录这段时间内的随机数。不在这个时间范围内的,就认为是以前的重放
     缺点:通信各方的计算机时钟保持同步,如果客户端和Server时间不同步。那么客户端可能需要使用Servertime进行认证了。
下图展示了一次,重放的过程


(上面图片来自百度百科)

第三层、数字签名

这里举例:选择明文攻击。简单的说就是如果Server也被假冒了怎么办

摘要认证的客户端会用服务器提供的随机数来生成response。但是,如果中间被恶意代理拦截,代理伪装Server给客户端发送随机数。这个是可怕的,假如代理有一个常用摘要字典,可以将客户端生成的摘要,反解出来密码。这种攻击叫“选择明文攻击chosen plaintext attack”,我觉得名字起的不好,不能顾名思义。

    防止此类攻击的办法可以通过“随机数”+“非对称加密”来实现,比如RSA非对称加密算法。如下图时序图:


    从我项目经验来看,客户端对signature的验证使用的publickey,并不是服务器应答中的public-key,而是一个内置的hexString(可以转换成内置公钥)。事实上server也有一个内置的私钥,server第一步应答是用的内置私钥加密。然后客户端验证。至于public-key每次登陆都不同(server有一个素数池子,可以快速生成私钥和公钥)。
其实,sNoce、cNonce和密码这个搭配,跟https的三个随机数【点击】类似
更详细见 http://blog.csdn.net/hherima/article/details/31356575

三、如何建立安全socket连接,其他措施

● 机器指纹,指的是手机的IMEI,model,IMSI等等信息。虽然按照隐私法,IM软件提供商,不能这么做。但是,为了安全都这么做。

● 图形验证码

    1.山寨客户端 、恶意客户端会频繁发送登录信令,造成Server堵塞,需要图验拦截。

    2.有些IM软件可以用手机号登录,可能要给手机发短信密码,这种情况,就会出现短信炸弹的问题。一个人用IM客户端输入别人的号码,使劲儿下发短信密码。

● IP地址收集。

● 更安全的做法。

例如:QQ设备锁,应该是基于机器指纹,在Server端存储,限制登录。


查阅了一些资料后发现:要像建立安全连接,防止重放是必须要做的,防止重放可以通过随机数来解决。

还有就是中间代理的一些漏洞,这些比较不好处理,可以增加双方验证来解决!就像刚刚将到的“选择明文攻击”。

四、趣事、bug

问题描述:

    1. 用户输入错误密码过多,客户端弹出图形验证码。

    2. 用户输入正确的密码,结果仍弹出图验,

    3. 用户再次输入正确密码,还是提示图形验证码,死循环了,一直图验。

    把bug告知Server了。

Server的解释是这样的:“登录信令中,如果'时间错误'(402)和'图验错'(421)一起出现,Server只会应答421,图验最优先”,客户端正是因为“时间错误和图验”一并出现,导致死循环。

    ● 客户端的逻辑:收到421应答,去下载图验。用户点击登录(仍携带本地错误时间);Server应答402,同时包括Server时间值。

    ● 客户端立即使用Server时间,再登录(此次不会带图验),Server仍会返回421需要图验,

    ● 最后,死循环了。

解决办法:

    ● 客户端这么修改:客户端收到402的时候,再去请求图验。客户端开发不太同意,原因有两个:

        1. 首先要记录上次的登录状态,如果是图验,还要再次下载图验,逻辑复杂。

        2. 客户端体验也不好,会弹出两次图验。第一次是正常的图验,第二次是客户端收到402后,再次下载图验。

    ● Server这么修改:Server在421的时候将Server时间一并下发,客户端使用正确的Server时间和图验实现登录。客户端改动小!

最终是Server做出了修改。

● 2013年,我在用百度音乐登录的时候,也曾遇到类似的问题,总是提示我图形验证码。估计是类似的问题。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
// C/C++, 利用OpenSSL库对Socket传输进行安全加密(RSA+AES) // 1. 利用RSA安全传输AES生成密钥所需的Seed(32字节) // 2. 利用AES_encrypt/AES_decrypt对Socket上面的业务数据进行AES加密/解密 // --- // * 理论上只需要AES就能保证全部流程,但由于AES加密所需要的AES-KEY是一个结构 // * 这个一个结构,如果通过网络进行传输,就需要对它进行网络编码,OpenSSL里面没有现成的API // * 所以就引入RSA来完成首次安全传输,保证Seed不会被窃听 // * 同样,只使用RSA也能完成全部流程,但由于RSA的处理效率比AES低, // * 所以在业务数据传输加密上还是使用AES // --- // 下面的代码包含了上述传输加密流程所需的所有步骤(OpenSSL部分) // 在实际的Socket应用开发时,需要将这些步骤插入到Client/Server网络通信的特定阶段 // --- // 为能完成代码的编译和执行,需要先安装OpenSSL执行库及开发库 // 以Debian为例,需要安装openssl 和 libssl-dev // 编译命令: g++ -o rsa-encrypt rsa-encrypt.cpp -lcrypto // --- // 所需的OpenSSL主要的API及功能描述 // 1. RSA_generate_key() 随机生成一个RSA密钥对,供RSA加密/解密使用 // 2. i2d_RSAPublicKey() 将RSA密钥对里面的公钥提出到一个BUF,用于网络传输给对方 // 3. d2i_RSAPublicKey() 将从网络传过来的公钥信息生成一个加密使用的RSA(它里面只有公钥) // 4. RSA_public_encrypt() 使用RSA的公钥对数据进行加密 // 5. RSA_private_decrypt() 使用RSA的私钥对数据进行加密 // 6. AES_set_encrypt_key() 根据Seed生成AES密钥对中的加密密钥 // 7. AES_set_decrypt_key() 根据Seed生成AES密钥对中的解密密钥 // 8. AES_encrypt() 使用AES加密密钥对数据进行加密 // 9. AES_decrypt() 使用AES解密密钥对数据进行解密 // --- // 一个典型的安全Socket建立流程, 其实就是如何将Server随机Seed安全发给Client // -- C: Client S:Server // C: RSA_generate_key() --> RSAKey --> i2d_RSAPublicKey(RSAKey) --> RSAPublicKey // C: Send(RSAPublicKey) TO Server // S: Recv() --> RSAPublicKey --> d2i_RSAPublicKey(RSAPublicKey) --> RSAKey // S: Rand() --> Seed --> RSA_public_encrypt(RSAKey, Seed) --> EncryptedSeed // S: Send(EncryptedSeed) TO Client // C: Recv() --> EncryptedSeed --> RSA_private_decrypt(RSAKey, EncryptedSeed) --> Seed // --- 到此, Client和Server已经完成完成传输Seed的处理 // --- 后面的流程是它们怎样使用这个Seed来进行业务数据的安全传输 // C: AES_set_encrypt_key(Seed) --> AESEncryptKey // C: AES_set_decrypt_key(Seed) --> AESDecryptKey // S: AES_set_encrypt_key(Seed) --> AESEncryptKey // S: AES_set_decrypt_key(Seed) --> AESDecryptKey // --- Client传输数据给Server // C: AES_encrypt(AESEncryptKey, Data) --> EncryptedData --> Send() --> Server // S: Recv() --> EncryptedData --> AES_decrypt(AESDecryptKey, EncryptedData) --> Data // --- Server传输数据给Client // S: AES_encrypt(AESEncryptKey, Data) --> EncryptedData --> Send() --> Client // C: Recv() --> EncryptedData --> AES_decrypt(AESDecryptKey, EncryptedData) --> Data / ========================================================================================= */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值