在你辛辛苦苦做完一个需要上传积分排行或领奖的游戏后,通常会遇到一个问题,有人利用H5游戏AJAX接口的开放性作弊刷分。设置POST保护、限制Refer这些常规手段被对手破解的时候,不如一开始就设置好数据传输加解密的机制,把作弊者消灭在摇篮里。
C2的数据传输加解密一般有两个插件,一个是AJAXRSA,另一个是Rex大神的XOR。前者每次传输都需要交换两次数据,消耗较大,因此,这里专门讨论一下XOR插件的解决方案。
首先,工具:XOR加密需要用到Rex的插件XOR,以及对应的服务端代码,为PHP代码示例:(代码原帖地址)
function xor_enc($str,$key){ $crytxt = ''; $keylen = strlen($key); for($i=0;$i<strlen($str);$i++){ $k = $i%$keylen; $crytxt .= $str[$i] ^ $key[$k]; } return $crytxt; }
XOR是一种通过密钥来进行的可逆的加密手段,客户端和服务端各保存一个相同的密钥,传输时先对数据进行加密,获得数据的一方再用同样的密钥进行解密即可。
那么,如何通过加密提高得分数据传递的安全性呢?如果只是简单的对传输数据进行加密,并不能解决作弊问题。例如:原提交数据为userid=abc&score=50,如果仅加密score,结果虽然变成了userid=abc&score=XORxxxxx(加密后字串),作弊者无法修改提交的分数,但仍然可以直接刷这一条数据,获得无限个50分。
于是我们可以在每局游戏(假设游戏以局为单位)开始时,服务器生成一个本局的gameid给客户端保存,客户端在本局完成提交分数时将gameid一并提交,数据为userid=abc&gameid=123&score=XORxxxxx,并且,服务器将比对是否存在这个gameid,比对成功后才给用户增加积分,这样就解决了作弊者用一个单一链接直接刷分的问题。但问题仍然存在,作弊者会很容易发现gameid的规律,每次启动一局游戏,服务器就会返回新的gameid,只要拿到这个gameid,再组合上其他两个参数(userid和加密过的score)就可以继续刷分,只是原先是1条连接刷分,现在需要用2条,刷分的风险仍然存在,只是成本提高了。
继续改进这个方法,服务器每次发下gameid后,客户端在提交得分时,将gameid和score用逗号分割符拼成一个字串,然后再用XOR加密,合成一个叫data的参数发回给服务端,类似于userid=abc&data=XORxxxxxx,服务端取到之后解密得到的是 gameid,score 这样的格式,通过拆分字符串,即可得到用于验证的gameid和score。虽然只是简单的调整,但作弊者将再也无法找到向服务器取到的gameid和发送的data之间的规律。更进一步,服务器可以将gameid在发送给客户端时就进行一次加密,这样连向服务端取到的gameid也会呈现出没有规律的样子,破解的成本会更高。再进一步,服务端在生成gameid时,可以和用户的userid进行一一对应,gameid唯一不重复,这样就可以通过客户端回传的gameid来找到应该把分数加给谁,即是说,客户端在提交游戏成绩时不再需要提交userid了,直接提交data=XORxxxxxx即可。
总结下来就是以下几点:
这样做的其他问题:毕竟是JS代码,虽然C2会做混淆压缩,但客户端的密钥就保存在data.js中,密钥和加密算法其实等于是对作弊者公开的。因此这些手段只是增加作弊者作弊的成本,并不能彻底杜绝作弊问题。解决作弊的最终方法还是采用状态同步甚至帧同步将玩家操作同步给服务端,由服务端来计算玩家积分和奖励。