游戏服务器在设计和实现上的安全性问题主要可以分为以下几个大类:
协议安全
流程安全
数据存储安全
需要注意的是,安全性和效率以及流程的复杂程度往往是对立的,越是安全的系统,流程越是复杂,效率也越低。我们在游戏的开发过程中需要做出折中的选择,达到安全性和效率的平衡。
协议安全
协议安全所指的是网络数据包的安全,通常意义上讲就是指网络数据包的加密和解密,由于网络数据包传输频繁,如果使用复杂的加解密技术将会带来巨大的CPU开销,另外由于客户端程序内存有数据包加密和解密的完整代码,因此无论多复杂的加解密算法都不能阻止破解数据包事件的发生。建议对于游戏中的一般网络包不需要进行复杂的加解密过程,只对数据包进行简单编码或者不加密也是可以接受的。
使用不对称的加密算法,客户端的代码中不能包含解密的算法和密钥。
保证客户端每次加密后的数据是动态的,以防止网络数据包被它人录制以后可以再次成功使用。例如,客户端有很复杂的加密过程,并且没有解密算法,但是如果加密过程没有动态性,由于每次输入相同的用户名加密码生成的数据包是完全相同的,那么如果被别人录制了登录的数据包,就可以伪造成功的登录。在不考虑使用密保等具有动态特性的组件以外,我们也可以考虑使用验证码的机制,新建立连接以后,服务器会发送当前有效的一段特征码,客户端加密数据需要使用到这一段特征码。这样即使登录数据被别人录制以后也无法直接使用,因为新建立的连接特征码是不同的。
另外提一下关于服务器加入部分反外挂特性的功能。一般游戏服务器内部主要能够集成的反外挂方式是防止加速外挂。目前常用的反加速外挂主要有两种:
一种方式是在客户端连接服务器后,服务器会把当前的时间发送到客户端,以后客户端发送的 每个数据包都需要包含自己计算出的服务器当前时间信息,服务器如果检测到数据中描述的服务器时间 超过了实际的时间,就认为玩家有可能使用了加速外挂。这种方式相对服务器负担不大,也不容易出错,但是只能防止变速齿轮等通用外挂,对于能够自己构造协议的外挂没有效果。
另外一种方式是服务器(一般是网关)上根据玩家发送的数据包类型进行分类,同类的数据包相互之间间隔必须满足一定条件,如果玩家连续多次出现同类数据包间隔过短的情况,就会被判定为使用加速器的玩家。这种模式虽然相对比较耗费服务器资源且需要关注到游戏逻辑,但是判断比较准确,基本上能防止各种加速方式。
在开发协议函数时,需要对所有的协议参数都进行合法性检查,服务器能够通过上下文获取的数据都不应该由客户端来提供。
流程安全
玩家游戏中的流程安全是相当重要的一个部分。对于一般的游戏,建议最好的方式是尽量维持使用同一条经过验证的Socket(Tcp)连接进行网络通信,因为相对来说想替换别人的网络数据是一件比较困难的事,除非玩家自己的设备中了木马。说简单一点就是尽量使用同一条TCP连接,先做登录验证,然后进行游戏,中间尽量不做切换连接的流程。但是对于实际的游戏而言并不一定能完全实现游戏过程中不切换连接。有些简单的游戏就没有网关的设计,如果切换线路服务器,必然会重新进行连接。还有部分游戏由于游戏过程是基于UDP或者Http短连接的通信模型,这些模式都需要重新验证玩家的有效性。如果需要有多次验证的系统请尽量满足下面几个原则:
同一个SessionID(或者称为accessToken)最好只能通过一次验证。如果游戏类型是切换游戏服务器、线路服务器或者是进入副本服务器时需要重新验证的类型,建议每次验证的SessionID都是重新生成的。如果条件允许的话,SessionID最好还包含有效期限制,但是这个需要针对不同类型的游戏有所不同。
遇到重复登录时候,一定要先确定新用户是否合法,然后再处理老的用户。否则就会出现没有验证新用户的有效性,直接就把老用户踢掉了的设计,这样会造成整个系统的玩家都可以被别人随便踢下线。
数据存储安全
游戏最重要的部分是玩家游戏数据的安全性,必须要保证用户数据能够实时安全的保存。
玩家切换服务器时,必须保证切换前数据已经被成功保存之后,才能被新的服务器读取。有部分游戏设计中,在切换服务器时,不进行实际存盘动作,只是将数据缓存在DBServer上,这个模式也可以接受,但是需要注意的是如果过程中发生切换服务器失败,玩家掉线等错误状态,DBServer要能够合理的将数据保存。
出现重复登录的情况,在踢出老用户的同时需要注意保存用户数据。建议重复登录的同时,最好也拒绝掉本次新的登录,确保用户数据的成功保存、以及老用户临时数据的清理。
玩家交易等过程一定要有事务的特性,两个玩家一定要完成物品的交换之后才可能发生写入数据库的操作,这个对于单线程逻辑程序一般很少有问题,但是对于多线程逻辑的程序必须注意。
如果处理数据库的读取和写入操作采用连接池的操作方式,需要注意单个用户数据的事务性和顺序性。可以通过一个用户数据只会在一个数据库连接上处理的方式来进行限制。
如果数据库发生故障或者玩家数据异常时,需要通过玩家的操作日志让玩家的数据能回滚到一个正常的值。
提供合理的数据库备份机制,目前一般的做法是利用数据库的主从备份机制,每天凌晨对从库进行全量备份,在数据库需要回滚到某个时间点t1的时候,可以每天的全量备份数据为基础,再执行凌晨到t1的binlog。不过,现在很多云服务商都提供了这样的服务,操作起来非常便利。比如网域云的RDS服务。