登陆认证系统
最近在忙代理项目 狂刃 的事情。
因为这个项目,我们提前建立了平台开发团队。但许多东西开始的都很仓促,比如需要对接用户登陆认证系统。
虽然已经有很多成熟的认证协议,比如最有名的 kerberos 。但这次时间紧迫,我就临时设计了一个简单协议。
因为不是 web 应用接入,所以我不想直接使用 https 来提交用户名和密码,而基于 http 协议,在不安全信道上建立了一个自定义协议来应付一下。这种临时设计的协议当然不会很缜密,但也基本够用。
这样,合作方的客户端可以较容易实现相应模块。
在整个业务中,有三个实体:
C 玩家客户端 G 游戏服务器 E 我们的认证平台
登陆流程是这样的:
- G 发现有 C 企图登陆时,生成一个一次性的 salt (如果直接用时间的话,可选对称加密),然后发送给 C
2, C 收到 salt ,利用自己的用户密码,对 salt 加密并签名。加密可以使用标准的 DES 算法,签名可以用标准的 MD5 算法。具体方法是:讲用户密码先 md5 hash 一次,得到一个串。取串的前一半做 DES 加密算法的 key 加密 salt ;然后把结果连接上密码 hash 串的后半部分,一起做一次 MD5 。密文和签名连接在一起,最终会得到一个加过签名的密文 secret 。
C 将第 2 步生成的 secret 连同自己的登陆名,以及需要登陆的游戏名发送给 E 。
E 收到 secret 后,从登陆名查询到用户信息,用保存在 E 上的密码做反向操作,验证签名是否正确。若正确则解出 salt 。失败则发送错误信息给 C ,登陆流程结束。
E 利用解开的 salt ,附加上用户 id 。利用游戏名查询到事先和 G 约定的游戏服务器密码,做同样流程的加密签名处理,发送回 C 。
C 转发认证密文回给 G 。G 用约定要的服务器密码,校验签名并解密,核对 salt 是否是在步骤 1 里发送出去的 salt ,确认用户的合法性。
整个流程,G 和 E 不必保持通讯,只需要事先约定要密码。这可视做一个简化版的 kerberos 协议,其中有一些安全隐患这里不一一指出。目前暂且可用。日后再来完善。
G 和 E 不保持通讯有一个好处:G 不需要对 C 保持连接状态,而只需要最终检查一下 C 发过来的认证包就可以知道认证是否通过。
设计完毕后,我用 C 实现了基本的校验认证函数。老马同学整合到他用的 web server 中,完成了认证服务器。并做了压力测试,结果还不错。
可惜合作方是用 Windows 做开发的,不方便联调。他们在开发期不希望依赖我们的用户系统,而我们的平台系统即使给他们,在 windows 下短期也较难配置起来。
我一拍脑袋,决定帮合作方现写一个认证用 web server 在 windows 下跑。对于我来说,最称手的某过于 lua 了。利用现成的 lua socket 模块,整个认证服务器只需要不到 100 行代码 。把用户名密码直接配置在 lua 源代码中,放到一个 table 中即可,数据库都不需要。反正是调试嘛。
一开始图简单,直接 bind/listen 了 web 端口,然后没 accept 一个认证请求,就处理一个。用阻塞方式工作。我觉得也就是调试一下流程,调通了后就可以用老马做好的正式认证服务器了。
可写完之后,手又痒了。感觉不支持并发很看不过眼,赶紧重写了。
幸亏这次重写,让事后扛住了一次压力。
昨天,合作方花前请了一个公会 600 人帮忙做了一次压力测试。这次测试他们认为是自己的事情,就没有通知我们。结果因为要用他们自己的用户系统(直接对公会放号),就没有联系老马,而是用了我那个拍脑袋临时写的测试用认证服务器。
还好做了并发处理,没在这个问题上翻船。不过昨天还是出了点小事故,在晚饭间接了好几个电话才搞定。
由于一开始考虑到是测试用,我把服务器 bind 地址默认配置成了 127.0.0.1 。结果合作方直接改成了服务器的配置 ip 。然后许多玩家认证不过,客户端编写的时候又没有足够的提示信息。搞了老半天才清楚,服务器配有电信和网通双线,所以是有双 IP 的。最后配置改为 0.0.0.0 搞定了这个问题。
12 月 12 日补充:
关于中间人攻击的问题:
我们需要防范用中间人代理玩家的所有通讯,在玩家完成认证后,取代玩家做一些事情。
为了防止玩家和游戏服务器间的通讯包被篡改,我们需要对游戏服务器和玩家客户端间的通讯协议加密。加密的首要步骤,就是在握手时,交换一个秘密,之后用这个密码对数据加密。
为了安全的交换密码, SSL 这种协议使用的是非对称加密,以及 CA 信任链完成的。但我们这个应用环境下,却有一个额外的优势:玩家和服务器间已经有了一个秘密(用户密码),不需要用复杂的方式交换。
但直接用用户密码做通讯加密是不可行的。因为游戏服务器并不知道这个密码,只是认证服务器知道。
那么,可以这样:在认证环节,认证通过后,认证服务器产生一个随机数,并同时用玩家密码和服务器密码加密两份,返回给玩家。
如果这个环节存在中间人,中间人不可能知道随机数,也无法伪造出两份密文蕴含同样的随机数。
玩家解出属于他的通讯密码(随机数),并把另一份转交给服务器。这样,游戏服务器和玩家间就安全的交换的通讯密码了。
Comments
Posted by: mathewes | (28) July 1, 2014 07:28 PM
Posted by: 好笔头业务云笔记 | (27) October 8, 2013 11:52 AM
Posted by: chlaws | (26) January 7, 2013 09:40 PM
Posted by: babam | (25) January 4, 2013 04:42 PM
Posted by: Cloud | (24) December 12, 2012 09:35 PM
Posted by: Daly | (23) December 12, 2012 06:37 PM
Posted by: Cloud | (22) December 12, 2012 06:18 PM
Posted by: Daly | (21) December 12, 2012 05:48 PM
Posted by: asking | (20) December 12, 2012 05:17 PM
Posted by: Cloud | (19) December 12, 2012 02:50 PM
Posted by: asking | (18) December 12, 2012 02:06 PM
Posted by: Daly | (17) December 12, 2012 10:01 AM
Posted by: Daly | (16) December 12, 2012 09:48 AM
Posted by: 幽灵 | (15) December 9, 2012 07:53 PM
Posted by: 幽灵 | (14) December 9, 2012 07:53 PM
Posted by: Cloud | (13) December 8, 2012 09:17 PM
Posted by: 宝刀屠龙 | (12) December 8, 2012 05:46 PM
Posted by: Cloud | (11) December 8, 2012 10:51 AM
Posted by: Tony Huang | (10) December 7, 2012 10:51 PM
Posted by: Tony Huang | (9) December 7, 2012 10:49 PM
Posted by: Cloud | (8) December 7, 2012 10:42 PM
Posted by: Tony Huang | (7) December 7, 2012 10:36 PM
Posted by: Anonymous | (6) December 7, 2012 10:29 PM
Posted by: 宝刀屠龙 | (5) December 7, 2012 07:05 PM
Posted by: Cloud | (4) December 7, 2012 06:14 PM
Posted by: zjxuejun | (3) December 7, 2012 05:53 PM
Posted by: Cloud | (2) December 7, 2012 04:15 PM
Posted by: 天浪 | (1) December 7, 2012 04:10 PM