You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何计算页面加载的所有JS文件哈希以验证HTML5游戏运行时完整性

嘿,这个问题在防游戏篡改刷分的场景里太常见了!结合你用Construct2开发、运行在iframe里的情况,我给你梳理一套实用的方案,从预生成哈希到启动/运行时检测,再到规避漏洞的注意事项都有:

第一步:预先生成游戏核心JS文件的原始哈希值

首先你得拿到导出后完全未被篡改的Construct2核心JS文件(通常是c2runtime.js,或者你自定义命名的游戏主JS),用SHA-256这类安全的哈希算法计算它的哈希值,把这个值存在你的服务器数据库里——绝对别存在客户端,不然玩家连哈希值一起改了就白搭。

生成哈希的简单方式:

  • 用Node.js命令行直接生成:
    node -e "const crypto = require('crypto'); const fs = require('fs'); const hash = crypto.createHash('sha256').update(fs.readFileSync('./c2runtime.js')).digest('hex'); console.log(hash);"
    
  • 或者本地用哈希工具计算,全程别把文件传到第三方平台,避免泄露。
第二步:游戏启动时的哈希检测

根据你iframe的运行环境,有两种可行的检测方案:

方案A:父页面主导检测(推荐,更难被绕过)

如果承载iframe的父页面是你可控的,直接在父页面里写检测逻辑,等iframe加载完成后验证游戏文件:

// 父页面代码,等待iframe加载完成后执行
document.getElementById('game-iframe').addEventListener('load', async () => {
  const iframe = document.getElementById('game-iframe');
  // 找到iframe内的核心游戏脚本
  const gameScript = iframe.contentWindow.document.querySelector('script[src*="c2runtime.js"]');
  if (!gameScript) return;

  // 加载脚本内容(加时间戳避免浏览器缓存旧文件)
  const response = await fetch(`${gameScript.src}?t=${Date.now()}`);
  const scriptContent = await response.text();

  // 计算SHA-256哈希
  const encoder = new TextEncoder();
  const data = encoder.encode(scriptContent);
  const hashBuffer = await crypto.subtle.digest('SHA-256', data);
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');

  // 向服务端请求原始哈希并对比
  const validHash = await fetch('/api/get-game-hash').then(res => res.text());
  if (hashHex !== validHash) {
    // 检测到篡改后的处理:比如禁用分数提交、提示用户、重置iframe
    alert('检测到游戏文件被篡改,无法继续游戏!');
    iframe.src = '';
  }
});

方案B:在Construct2内部嵌入检测逻辑

如果想把检测逻辑放在游戏本身,用Construct2的「JavaScript代码」动作实现:

  1. 在Construct2事件表中添加「游戏启动时」的触发事件
  2. 添加「运行JavaScript」动作,插入以下代码:
// 加载核心游戏脚本(注意路径要和导出后的文件结构匹配)
fetch(`c2runtime.js?t=${Date.now()}`)
  .then(res => res.text())
  .then(scriptContent => {
    const encoder = new TextEncoder();
    const data = encoder.encode(scriptContent);
    return crypto.subtle.digest('SHA-256', data);
  })
  .then(hashBuffer => {
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    // 把计算出的哈希发给服务端验证
    return fetch('/api/verify-game-hash', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ hash: hashHex })
    });
  })
  .then(res => res.json())
  .then(result => {
    if (!result.valid) {
      // 标记篡改状态,后续提交分数时拦截
      runtime.globalVars.isTampered = true;
      // 调用你在Construct2里做的警告UI
      runtime.callFunction('ShowTamperWarning');
    }
  });

之后在游戏结束提交分数的逻辑里,先检查全局变量isTampered,如果为true就禁止发送分数请求。

第三步:运行时周期性检测(可选)

如果担心玩家在游戏过程中篡改文件,可以加个定时检测:

  • 在Construct2里添加「每10秒」的触发事件,重复执行上面的哈希检测逻辑
  • 或者在父页面设置定时器,每隔一段时间重新验证一次iframe内的脚本哈希
关键注意事项(避坑指南)
  • 混淆你的JS文件:先用Terser或UglifyJS把Construct2导出的JS混淆一遍,既增加篡改难度,也能避免代码格式化导致哈希变化
  • 服务端验证是核心:所有哈希对比必须在服务端做,绝对不能把原始哈希硬编码在客户端JS里
  • 双重验证分数:除了哈希检测,服务端还要验证分数合理性——比如某关卡最高分不可能超过10000,或者分数增长速度符合正常游戏节奏,防止玩家绕过哈希检测直接发假分数请求
  • 防范检测逻辑被篡改:技术高的玩家可能会替换crypto.subtle.digest这类函数返回假哈希,建议把父页面的检测JS也混淆,同时尽量减少客户端检测逻辑的暴露

内容的提问来源于stack exchange,提问作者Tom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 09:47:32