在线网页游戏轻量反作弊机制开发及排行榜防篡改方案咨询
小游戏排行榜防作弊解决方案
核心结论
服务端全量校验不是唯一的解决方案,你可以根据排行榜的权威性需求,选择多层轻量校验方案,几乎不会增加服务器算力压力,就能挡住99%的普通用户作弊行为。
可落地的低成本方案
- 加签验签+关联参数校验
客户端上传分数时不能仅提交得分,需要同时上传和分数强关联的游戏过程核心指标(比如总游玩时长、躲避障碍物数量、拾取道具数量等),再将所有参数排序拼接后,和服务端约定的密钥混合生成签名一同上传。服务端收到请求后先校验签名是否合法,再校验得分和关联参数的逻辑是否匹配(比如正常游玩每分钟最多获得1000分,提交10秒获得10000分的请求直接驳回),可以直接拦住所有手动发自定义POST请求的作弊行为。 - 高分阈值复审
设定合理的历史最高分阈值,超过阈值的分数不直接进入公开排行榜,而是存入待审核队列,仅针对前10名的高分做人工/半自动校验即可,普通段位的分数作弊不会影响排行榜的整体公平性,也不会产生额外的服务器开销。 - 前端反调试+代码混淆
对游戏核心逻辑代码做混淆加密,添加开发者工具检测逻辑,检测到调试状态直接暂停游戏或上报作弊标记,避免普通用户直接找到碰撞器、分数变量的位置做修改。
现有代码修改示例
const express = require('express'); const crypto = require('crypto'); const app = express(); const fs = require('fs'); app.use(express.json()); app.use(express.urlencoded({extended:true})); app.use(express.static('Build')) // 自定义签名密钥,不要对外泄露 const SIGN_SECRET = 'your_game_custom_secret_2024'; // 游戏数值规则,可根据实际玩法调整 const MAX_SCORE_PER_SECOND = 30; const MAX_OBSTACLE_PER_SECOND = 2; // 签名校验工具函数 function verifyValid(params, clientSign) { const sortedKeys = Object.keys(params).sort(); const signStr = sortedKeys.map(key => `${key}=${params[key]}`).join('&') + SIGN_SECRET; const serverSign = crypto.createHash('md5').update(signStr).digest('hex'); return serverSign === clientSign; } app.get('/leaderBoard', (req,res)=>{ var rawData = fs.readFileSync('./database.json'); var data = JSON.parse(rawData); res.json(data); }) app.post('/leaderBoard', (req, res)=>{ const { score, playTime, obstaclePassed, sign } = req.body; // 校验参数完整性 if (!score || !playTime || !obstaclePassed || !sign) { return res.status(400).json({ status: 'fail', msg: '参数缺失' }); } // 校验签名合法性 if (!verifyValid({ score, playTime, obstaclePassed }, sign)) { return res.status(403).json({ status: 'fail', msg: '非法请求' }); } // 校验数值逻辑合理性 if (score > playTime * MAX_SCORE_PER_SECOND || obstaclePassed > playTime * MAX_OBSTACLE_PER_SECOND) { return res.status(403).json({ status: 'fail', msg: '分数异常' }); } var rawData = fs.readFileSync('./database.json'); var leaderBoard = JSON.parse(rawData).leaderBoard; for(var i = 0; i < leaderBoard.length; i++){ if(req.body.score > leaderBoard[i].score){ for(let i2 = leaderBoard.length -1; i2 > i; i2--) leaderBoard[i2] = leaderBoard[i2-1]; leaderBoard[i] = req.body; break; } } fs.writeFileSync("./database.json",JSON.stringify({leaderBoard},null,2)); res.json({ status: 'success', msg: '分数上传成功' }); }) app.listen(process.env.PORT || 3000);
内容的提问来源于stack exchange,提问作者Nati Reitblat
相关产品推荐
相关产品推荐

