SBTI为什么能霸屏朋友圈?项目分析|附源码

admin 2026-04-13 05:25:21 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 该文档对SBTI前端静态项目进行源码分析,项目为单页人格测试页面,包含30道题目和27种人格结果。核心发现包括:结果完全在前端计算,题目顺序随机不影响分数;存在大量模板并列情况但缺乏明确决胜规则;安全审计未发现高危漏洞但指出innerHTML潜在XSS风险。建议增加排序决胜规则、改用安全DOM操作方式,若用于正式业务需将判分逻辑后移。 综合评分: 85 文章分类: 代码审计,WEB安全,安全工具,安全开发


cover_image

SBTI为什么能霸屏朋友圈?项目分析|附源码

原创

北境 北境

0xArgus

2026年4月10日 13:25 北京

后台回复 SBTI 获取源码。

SBTI 项目源码分析报告

1. 项目概况

本次分析对象为单页静态项目,主文件为 ce.html,配套素材位于 image/ 目录。代码没有构建脚本、依赖清单、后端接口、数据库配置,也没有账号、会话、持久化存储等常见应用层结构。

从代码体量和组织方式看,这个项目属于典型的前端本地计算页面:

✅ 页面结构、样式、题库、人格库、评分逻辑全部写在 ce.html。

✅ 结果图片通过本地路径映射加载,定义在 ce.html#L1256。

✅ 页面分为 introtestresult 三个区块,定义在 ce.html#L688、ce.html#L702、ce.html#L722。

这类实现部署简单,传播方便,维护压力也会集中到一个文件里。后续只要题目、结果库、显示规则再继续增长,单文件维护会变得吃力。

2. 本地搭建与使用方式

这个项目没有编译步骤,也没有后端依赖,本地使用门槛很低。按现有代码结构,下面三种方式都能跑起来。

2.1 直接打开文件

适用场景:

✅ 临时查看页面效果

✅ 本地快速核对题目和结果图

✅ 不需要联调接口

操作方式:

1进入项目目录。

2直接双击 ce.html。

3浏览器打开后即可开始测试。

说明:

✅ 当前项目使用的是本地相对路径图片,直接打开 HTML 文件通常可以正常显示。

✅ 这种方式最简单,适合单机查看。

2.2 用 Python 启动本地静态服务

适用场景:

✅ 想用 http://localhost 方式访问

✅ 想减少浏览器对 file:// 场景的差异影响

✅ 后续准备加脚本、接口代理或联调工具

在项目目录执行:

powershell● ● ●

python -m http.server 8080

启动后浏览器访问:

text● ● ●

http://localhost:8080/ce.html

如果机器上有多个 Python 版本,也可以用:

powershell● ● ●

py -m http.server 8080

2.3 用编辑器的静态服务插件启动

适用场景:

✅ 日常改页面和调样式

✅ 希望保存后自动刷新

常见做法:

✅ VS Code 安装 Live Server

✅ 在项目根目录右键 ce.html

✅ 选择 Open with Live Server

2.4 本地使用注意事项

✅ 页面没有数据库和本地存储,刷新后当前答题状态会清空。

✅ 图片资源依赖 image/ 目录,移动 ce.html 时需要一起保留素材目录结构。

✅ 当前项目没有构建产物概念,修改 ce.html 后重新刷新浏览器即可看到效果。

3. 页面怎么跑起来

3.1 启动流程

点击“开始测试”后,页面执行 startTest(false),入口事件绑定在 ce.html#L1732,函数定义在 ce.html#L1718。

startTest() 做了五件事:

1重置 app.answers

2打乱 30 道正式题的顺序。

3把第一道特殊题插入到随机位置。

4调用 renderQuestions() 生成题目 DOM。

5切换到测试页。

这里的随机性只作用在题目出现顺序和特殊题插入位置上,判分过程没有使用随机数。

3.2 数据结构

代码中有四组核心数据:

dimensionMeta:15 个评分维度,位置在 ce.html#L779 附近。

questions:30 道正式题,位置在 ce.html#L795。

specialQuestions:2 道特殊题,位置在 ce.html#L1067。

TYPE_LIBRARYNORMAL_TYPES:人格文案和人格模板,位置在 ce.html#L1092 与 ce.html#L1286。

当前可数出的数据规模如下:

| 项目 | 数量 | | — | — | | 正式题 | 30 | | 特殊题 | 2 | | 评分维度 | 15 | | 常规人格模板 | 25 | | 特殊人格 | 2 | | 结果图片映射 | 27 |

4. 结果是怎么算出来的

4.1 判分主线

核心计算函数是 computeResult(),定义在 ce.html#L1598。

这段逻辑可以拆成四步:

1把 30 道正式题的答案累加到 15 个维度上。

2把每个维度的原始分数压成 L / M / H 三档。

3把 15 维用户向量与 25 个常规人格模板逐一计算距离。

4取排序后的第一名作为常规最佳结果,再判断是否触发特殊覆盖。

4.2 维度分档

分档函数 sumToLevel() 位于 ce.html#L1580 附近,规则很直白:

<= 3 记为 L

= 4 记为 M

>= 5 记为 H

每个维度由两道题组成,每题分值是 1 / 2 / 3。因此每个维度都能独立落到 LMH 任意一档。

4.3 模板匹配

人格模板写成固定的 15 维模式串,例如:

CTRL -> HHH-HMH-MHH-HHH-MHM

BOSS -> HHH-HMH-MMH-HHH-LHL

THIN-K -> HHL-HMH-MLH-MHM-LHH

模板数据在 ce.html#L1286,解析函数 parsePattern() 在 ce.html#L1590。

计算时使用了一个非常常见的离散向量距离模型:

L = 1

M = 2

H = 3

然后逐维计算绝对差值,累加成 distance。完全命中的维度数记为 exact。相似度公式写在 ce.html#L1621:

js● ● ●

const similarity = Math.max(0, Math.round((1 - distance / 30) * 100));

这里的 30 来自理论最大距离:15 个维度,每维最大差值 2,总上限 30。

4.4 特殊覆盖

常规模板匹配完成后,代码还会检查两个特殊分支:

✅ 饮酒隐藏人格 DRUNK,触发条件见 ce.html#L1601 和 ce.html#L1640。

✅ 低匹配率兜底人格 HHHH,触发条件见 ce.html#L1646。

这两条逻辑会覆盖常规人格结果。代码表达得很清楚,作者也把它当作彩蛋和兜底机制在用。

5. 同样的答案会不会得出不同结果

这个问题要分两层看。

5.1 题目顺序随机,不影响分数

页面确实会随机打乱题目顺序,随机插入第一道特殊题,相关代码在 ce.html#L1494 和 ce.html#L1718。

这部分只影响用户看到题目的顺序,不影响最终分数:

app.answers 按题目 id 存储。

computeResult() 遍历的是固定的 questions 数组。

✅ 维度累计只认 q.id -> q.dim -> value,不认题目出现顺序。

结论:如果两次作答的每一道题答案都相同,题目顺序变化不会改结果。

5.2 代码里存在大量“并列第一”

真正需要注意的是排序逻辑。人格模板排序写在 ce.html#L1623:

js● ● ●

}).sort((a, b) => {
  if (a.distance !== b.distance) return a.distance - b.distance;
  if (b.exact !== a.exact) return b.exact - a.exact;
  return b.similarity - a.similarity;
});

问题在这里:

similarity 本身由 distance 推导出来。

✅ 当两个类型 distance 相同、exact 相同,similarity 也会相同。

✅ 这时比较器返回 0,代码没有再做最后一级 tie-break。

我把 15 维等级空间全部扫了一遍,一共 3^15 = 14,348,907 种可能向量。其中有 2,270,028 种向量在第一名位置出现并列。可以举两个具体例子:

✅ 向量 LLLLLLLLLLLLMMM 会在 IMSBDEAD 之间并列。

✅ 向量 LLLLLLLLLLLLHMH 会在 FUCKDEAD 之间并列。

这类向量都能被真实答案构造出来,因为每个维度的 L / M / H 都是可达的。

5.3 这会不会真的“同答案两次结果不同”

在当前主流浏览器里,Array.prototype.sort() 是稳定排序。同一份答案、同一份模板数组、同一运行环境下,结果通常能复现。并列时会落到 NORMAL_TYPES 中排得更靠前的那个类型。

代码层面仍然留着一个不确定点:

✅ 比较器没有把并列情况定死。

✅ 一旦运行环境不同,或者未来有人调整了 NORMAL_TYPES 的顺序,并列答案的结果就可能变化。

结论可以写得更准确一点:

✅ 当前版本在同一环境下通常可复现。

✅ 代码没有把并列模板的定序规则写死。

✅ 存在一批答案会同时命中多个第一名模板。

✅ 如果后续换运行环境、换模板顺序,结果可能发生变化。

如果希望结果完全可复现,建议在排序比较器最后增加一层明确规则,例如按 code 字典序,或按一个单独的 priority 字段排序。

6. 安全审计

6.1 审计结论

这份源码没有后端接口、没有鉴权、没有数据库、没有文件上传、没有命令执行,也没有发现 fetchXMLHttpRequestlocalStoragesessionStoragecookie 等状态或数据传输逻辑。站在 Web 安全角度,这个页面的攻击面很窄。

本次审计没有发现高危或中危漏洞。当前更接近“低风险实现问题 + 业务可信度问题”的组合。

6.2 发现项

发现 1:并列模板没有明确决胜规则

✅ 风险等级:低

✅ 类型:业务逻辑缺陷

✅ 位置:ce.html#L1623

说明:

排序只比较 distanceexactsimilarity 三项。并列时比较器返回 0。这个问题不会造成代码执行风险,但会影响结果解释的一致性,尤其是模板库继续扩充之后。

建议:

✅ 增加显式的第四排序键。

✅ 将并列规则写入注释或数据字段,方便后续维护。

发现 2:页面存在 innerHTML 渲染点

✅ 风险等级:低

✅ CWE:CWE-79

✅ 位置:ce.html#L1523、ce.html#L1672

说明:

renderQuestions()renderDimList() 使用了 innerHTML 拼接 DOM。当前数据源来自同文件内的常量,页面也没有接收外部输入,所以现阶段没有可直接利用的 XSS 入口。

这个写法的问题在于扩展性。一旦未来把题库、人格文案或维度说明改成接口返回、URL 参数、后台配置,innerHTML 会立刻变成风险点。

建议:

✅ 可控文本统一改用 textContent

✅ 需要生成结构时,优先使用 createElementappendChild

发现 3:评分规则全部暴露在前端

✅ 风险等级:低

✅ CWE:CWE-602

✅ 位置:ce.html#L1092、ce.html#L1286、ce.html#L1598

说明:

题库、模板、隐藏分支、图片映射、结果逻辑全部在客户端明文可读。对娱乐页面影响有限;如果后续用于付费测试、用户分层、招聘筛选、积分奖励,这套实现很容易被逆向和篡改。

建议:

✅ 若页面继续保持娱乐用途,这个问题可以接受。

✅ 若要承载正式业务,判分逻辑应迁移到后端,至少把模板库和结果策略从前端剥离。

6.3 未发现的常见风险

本次没有发现以下类型问题:

✅ SQL 注入

✅ 命令注入

✅ SSRF

✅ 文件上传类漏洞

✅ 身份认证绕过

✅ 会话固定

✅ 敏感凭据硬编码

原因很简单:当前项目根本没有这些能力,也就没有对应攻击面。

7. 代码层面的补充判断

还有两个实现细节值得单独记一下。

7.1 previewMode 目前没有入口

状态对象里保留了 previewMode,见 ce.html#L1472。页面代码只在 getQuestionMetaLabel() 里读取它,见 ce.html#L1514。

当前页面没有任何 UI 会把它切成 true。这更像一个未接线的调试开关。

7.2 五类模型更多是展示层概念

dimensionMeta 里给 15 个维度分了“自我模型”“情感模型”“态度模型”“行动驱力模型”“社交模型”,定义位置在 ce.html#L779 一带。

当前评分逻辑没有按这五类再做汇总、加权或单独输出。它们目前主要承担概念包装和结果说明的作用。

8. 建议

如果这个项目还要继续维护,我建议优先做四件事:

1给排序逻辑补上明确的 tie-break,先把结果一致性问题收住。

2把 innerHTML 改成更稳妥的 DOM 构造方式,提前消掉扩展阶段的 XSS 风险。

3把题库、人格库、图片映射拆到独立文件,降低单文件维护成本。

4如果未来要做正式测评、付费使用或业务分流,判分逻辑需要后移,前端当前这套实现承受不了可信度要求。

9. 总结

从源码来看,这个项目完成度不低,页面流程也顺。它的问题不在“跑不起来”,而在“结果解释能否长期站得住”。当前版本适合当娱乐型内容页使用,传播效率高,修改门槛低。只要继续沿用现有单文件实现,后面遇到的问题大概率会集中在三个方向:结果一致性、可维护性、业务可信度。

如果只回答最核心的问题,可以概括成三句话:

✅ 这个页面的结果完全在前端本地算出来,没有后台参与。

✅ 同一组答案在同一环境下通常会得到同一个结果,题目顺序随机不会改分。

✅ 模板并列的情况很多,代码没有写死最后的定序规则,这一点需要尽快补上。

🤖 灵思 · 你的AI信息触角 专注安全漏洞分析与AI前沿技术


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:0xArgus 北境 北境《SBTI为什么能霸屏朋友圈?项目分析|附源码》

评论:0   参与:  0