隐身检测与身份画像交叉分析
本页说明 MyIP 如何代理隐身检测结果查询、转发身份画像评估,以及通过浏览器侧 Cloudflare Trace 获取网络出口观测。重点是请求上下文、上游边界、结果语义和异常处理,而非推测未包含在本地实现中的评分算法。
目的与范围
本页覆盖 GET /api/invisibility、POST /api/persona/evaluate、probeTrace() 和两个后端处理器共用的超时请求机制。路由注册可见 backend-server.js。
“交叉分析”需要区分两层含义:
- 本地可确认的观测与传输:网络出口国家、连接协议等信号的采集,以及将调用者提交的画像和请求上下文发送到评估服务。
- 上游负责的解释与评分:身份画像处理器明确将评价交给主 IPCheck.ing API,本部署不包含该评价实现。现有证据不能证明隐身检测结果会自动作为画像评估输入,也不能给出权重、风险阈值或完整报告字段。
浏览器指纹、WebRTC、DNS 泄漏、登录界面和部署配置属于相邻主题,本页不展开其内部实现。当前运行上下文未提供这些主题的目录链接,因此不构造未经确认的 Wiki 地址。前端页面编排、完整画像 DTO、评分实现及持久化策略不在已读取源码范围内。
概述
这组能力提供两个不同的接口模型:
| 能力 | 输入 | 本地职责 | 输出 |
|---|---|---|---|
| 隐身检测查询 | 查询参数 id | 校验 ID、附加部署密钥、查询上游、转换部分状态 | 上游 JSON,或 pending/错误对象 |
| 身份画像评估 | 含真值 persona.country 的请求体 | 验证基本入口条件、重建请求头、转发完整 JSON | 保留上游 HTTP 状态的 JSON |
| 网络出口观测 | 无参数 | 从浏览器请求 Cloudflare Trace 并解析 | 可用性、国家、机房、HTTP 和 TLS 信息 |
身份画像处理器的注释描述了“一份 observation 输入,一份 graded report 输出”的约定,并指出结果由标识符和枚举组成,前端负责四语言文案。这是接口分工说明,不是本地评分模型定义。来源:persona.js。
架构
Sources: backend-server.js、invisibility-test.js、persona.js、fetch-with-timeout.js、probe-server.js。
图中刻意不连接 probeTrace() 与画像 POST 接口:已读代码证明探针存在,但未包含将探针结果装入请求体的前端调用链。浏览器探测与后端上游代理也是两条不同的网络路径,不能将后端服务器出口当成浏览器出口。
隐身检测:从 ID 到结果状态
输入验证与请求构造
处理器依次执行以下操作:
- 从
req.query.id取值;缺失或假值返回 HTTP 400,错误为No ID provided。 - 要求 ID 是长度恰为 28 的 ASCII 字母数字字符串;不满足则返回 HTTP 400,错误为
Invalid ID。 - 读取
IPCHECKING_API_KEY;缺失返回 HTTP 500,错误为API key is missing。 - 读取
IPCHECKING_API_ENDPOINT,拼接/getpdresult/{id}?apikey={apikey}。 - 调用
fetchUpstream(),将req.headers展开转发给上游。
严格限制 ID 的字符集也约束了其作为 URL 路径片段的形式,但代码没有证明它本身承担用户认证或授权功能。该入口只查询结果,不包含启动测试或生成 ID 的实现。来源:invisibility-test.js。
待完成、配额与登录状态
处理器不会把所有非 2xx 响应都当作系统错误。上游 404 的源码注释明确表示“结果尚未计算完成”,用于前端启动测试后的轮询阶段;本地把它转换为默认 HTTP 200 的 { status: 'pending' }。因此,HTTP 成功并不等于测试完成。
| 上游情况 | 本地响应 | 处理意义 |
|---|---|---|
| 404 | HTTP 200,status: 'pending' | 结果未就绪,不记错误日志 |
| 429 | HTTP 429,code: 'quota_exceeded' | 上游月度配额耗尽;error 优先使用上游值 |
| 401 或 403 | 保留状态,返回 error | 优先采用上游 message,否则 Sign in required |
| 其他非成功状态 | HTTP 500,error: error.message | 提取上游错误详情、抛错并记录日志 |
| 成功且 JSON 可解析 | 默认 HTTP 200,上游 JSON | 不重建报告结构 |
| 请求或 JSON 解析异常 | HTTP 500,error: error.message | 记录 invisibility-test upstream request failed |
来源:invisibility-test.js。前端轮询间隔、最大次数以及停止条件未在已读代码中确认。
身份画像:保留上下文的薄代理
校验顺序与边界
画像处理器先检查方法必须为 POST,再检查密钥和上游地址,最后检查 req.body?.persona?.country。方法不匹配返回 405;配置缺失返回 500;缺少真值 country 返回 400。
这里不是完整的数据验证:代码没有校验 country 是否为两字母国家码,也没有在本地限制其类型、枚举或其他 observation 字段。不能把该真值检查解释为完整 DTO 验证。构建的上游地址是 /persona/evaluate,密钥通过 URL.searchParams.set('key', apiKey) 加入。
来源:persona.js。
为什么需要重建请求头
评估服务需要调用者上下文,因此请求头原则上保留;但 callerHeaders() 先复制请求头,再删除 host、content-length、content-type、connection、transfer-encoding。处理器随后显式写入 Content-Type: application/json 并重新序列化请求体。
这使新请求的目标地址和实体编码不依赖上一跳的长度、主机及连接信息。它是代码中列出的五项过滤,而非可直接假定为完整的通用代理头清理器。隐身检测代理没有使用该过滤函数,二者行为不应混淆。来源:persona.js。
状态透传而非统一包装
请求成功到达上游后,不检查 apiResponse.ok,而是解析 JSON 并保留 apiResponse.status。JSON 无法解析时使用空对象 {},仍保留原 HTTP 状态。因此,上游拒绝和上游不可达是两类情况:前者保留状态与可解析载荷,后者被记录为 persona evaluate upstream fetch failed 并返回 HTTP 502、Upstream fetch failed。
Sources: backend-server.js、persona.js、fetch-with-timeout.js。
浏览器网络出口观测
probeTrace() 请求固定地址 https://cloudflare.com/cdn-cgi/trace。源码将其 loc 描述为出口国家的独立第二意见;它提供外部观察视角,而非证明一个人的真实所在地或身份。
parseTraceBody(text) 将输入转换为字符串、按换行分割,只处理第一个 = 位于非零位置的行。键和值分别去除两端空白,值内后续的 = 会保留;同名键由后面的行覆盖。HTTP 成功后,探针返回以下字段:
| 字段 | 来源/语义 | 缺失时 |
|---|---|---|
available | HTTP 成功且解析过程未抛错 | 成功路径为 true |
country | loc 转大写 | 空字符串 |
colo | Cloudflare 机房标识 | 空字符串 |
httpProtocol | http | 空字符串 |
tlsVersion | tls | 空字符串 |
非成功 HTTP 状态返回 { available: false, reason: 'http-状态码' };AbortError 返回 timeout,其他异常返回 unreachable。特别地,HTTP 200 但缺少 loc 仍得到 available: true,调用方必须区分“请求可用”和“国家字段有效”。源码:probe-server.js。
使用示例:实际实现片段
以下示例直接摘自仓库,展示接口处理方式,不是虚构的客户端调用或报告格式。
1. 隐身检测 ID 校验
1function isValidUserID(userID) {
2 if (typeof userID !== 'string') return false;
3 if (userID.length !== 28 || !/^[a-zA-Z0-9]+$/.test(userID)) return false;
4 return true;
5}Source: invisibility-test.js。
该检查会拒绝数组、数字、空格、标点和非 ASCII 字母,不会将输入自动转换成字符串。
2. 将尚未完成转换成业务状态
if (apiResponse.status === 404) {
return res.json({ status: 'pending' });
}Source: invisibility-test.js。
这是隐身检测与通用 HTTP 代理的关键差异:404 在此不是“查询接口不存在”,而是上游约定的未完成结果。
3. 转发画像 observation
1 const apiResponse = await fetchUpstream(url, {
2 method: 'POST',
3 headers: { ...callerHeaders(req), 'Content-Type': 'application/json' },
4 body: JSON.stringify(req.body),
5 });Source: persona.js。
代码转发整个请求体,不只转发 persona.country。因此后续增加 observation 字段时,仍需要核对上游契约,不能以本地能序列化为兼容性保证。
4. 带超时且不使用浏览器缓存的探测
1const withTimeout = async (url, options = {}) => {
2 const controller = new AbortController();
3 const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
4 try {
5 return await fetch(url, { cache: 'no-store', signal: controller.signal, ...options });
6 } finally {
7 clearTimeout(timer);
8 }
9};Source: probe-server.js。
当前 probeTrace() 不传 options,因此使用自身的取消信号和 no-store。若未来给该内部函数传入 signal 或 cache,后置展开的 options 会覆盖默认值;它不同于共享请求封装的取消信号合并机制。
配置与超时
| 配置/参数 | 类型 | 默认值 | 作用及限制 |
|---|---|---|---|
IPCHECKING_API_KEY | 环境变量字符串 | 无本地默认值 | 两个代理必需;隐身查询使用 apikey 查询参数,画像使用 key |
IPCHECKING_API_ENDPOINT | 环境变量字符串 | 无本地默认值 | 上游 API 基址;画像显式检查存在性,隐身查询不做该检查 |
fetchUpstream 的 timeoutMs | 数值,毫秒 | 8000 | 两个处理器没有覆盖它 |
fetchWithTimeout 的 timeoutMs | 数值,毫秒 | 5000 | 通用封装默认值,后端预设会覆盖为 8000 |
upstreamUserAgent | 运行时值 | null | 通过 setUpstreamUserAgent(ua) 设置,仅在调用者未携带 UA 时注入 |
TRACE_URL | 常量字符串 | https://cloudflare.com/cdn-cgi/trace | 浏览器出口观测地址,不是已确认的环境配置项 |
TIMEOUT_MS | 常量数值,毫秒 | 6000 | Trace 请求超时 |
来源:invisibility-test.js、persona.js、fetch-with-timeout.js、probe-server.js。
共享封装为每次调用创建独立 AbortController,同时监听调用者已有的 signal:已取消则立即取消新控制器,否则通过一次性监听器传播取消。定时器在 fetch 完成或失败后清除。需要注意,封装返回的是 Response,随后在处理器中执行的 response.json() 不处于这个定时器的保护范围内,因此不宜将 8 秒称为包含完整响应体读取的端到端总时限。
UA 注入会大小写不敏感地检查普通对象中的键,也支持 Headers.has();数组形式的 headers 被视为不注入。由于两种代理都会携带调用者请求头,已有浏览器 UA 不会被部署标识覆盖。来源:fetch-with-timeout.js。
API 参考
这些文件为 JavaScript,未声明 TypeScript 类型。下表中的返回说明来自实现行为,不是额外定义的类型契约。
| 接口/函数 | 参数 | 返回与失败行为 |
|---|---|---|
GET /api/invisibility | 查询参数 id,必需,28 位字母数字字符串 | 上游 JSON、pending 或错误 JSON;状态转换见前文 |
POST /api/persona/evaluate | JSON 请求体,至少 persona.country 为真值 | 上游状态与 JSON;本地 400/405/500,转发异常 502 |
probeTrace() | 无 | Promise,兑现为可用信息或带 reason 的不可用对象;内部捕获探测异常 |
parseTraceBody(text) | 可经 String(text) 转换的值 | 键值对象;不校验国家码或字段完整性 |
fetchWithTimeout(url, init = {}) | fetch 地址与选项,另支持 timeoutMs | Promise,成功返回 Response;异常继续向调用方传播 |
fetchUpstream(url, init = {}) | 同上 | 应用 8 秒预设及可选 UA 后返回底层 Promise |
setUpstreamUserAgent(ua) | UA 值,假值重置为 null | 无显式返回值;更新模块级配置 |
来源:invisibility-test.js、persona.js、probe-server.js、fetch-with-timeout.js。
故障、边界与并发
容易误判的边界
- URL 构造在 try 之外:两个处理器都在进入请求异常捕获前执行
new URL(...)。非法基址导致的 URL 构造异常不经过各自的 500/502 捕获分支;最终由哪一层处理,不能从这两个文件确定。 - 配置错误文案并不精确区分原因:画像缺密钥或缺基址都返回
API key is missing;排障时应同时检查两项。隐身查询只检查密钥存在性。 - JSON 失败策略不同:画像响应无法解析时使用
{};隐身查询的正常响应无法解析时进入 500 分支。画像的空对象不代表成功生成了空报告。 - 没有本地评分保证:画像中的 country 真值检查不能保证输入国家合法,也不能证明观测数据可信。
- 探测失败不是风险结论:Trace 的
timeout、unreachable或 HTTP 错误只能证明此次观测没有获得可用结果,不能直接推导为匿名成功、代理存在或身份欺骗。
这些差异来自 persona.js、invisibility-test.js 和 probe-server.js。
并发与数据生命周期
两个处理器均在请求内构造 URL 和响应,不在已读实现中写入数据库、缓存报告或维护任务队列。画像文件明确将评估描述为逐访问者且不缓存;Trace 请求显式使用 cache: 'no-store'。这些局部行为不能证明外部评估服务没有保存数据,也不能替代反向代理或部署层的缓存审计。
每次 fetch 的控制器、定时器和画像请求头副本都是独立的;没有观察到跨请求锁、合并请求、去重或重试。upstreamUserAgent 是共享模块变量,属于例外的全局运行时配置。身份画像请求体及保留的调用者请求头会离开当前部署发送到上游,隐身查询也会转发调用者请求头;这里的“隐身检测”不应被理解为不向任何第三方传输信息。
运维、性能与扩展
排障顺序
- 先分清业务状态与故障:隐身 200
pending是未完成;429 是上游配额;401/403 是登录相关状态。不要仅按非 2xx 数量评估系统故障。 - 确认配置与 URL:检查密钥、基址和 URL 合法性;画像密钥参数名与隐身查询不同。
- 查看指定日志:隐身查询异常记录
invisibility-test upstream request failed,画像请求异常记录persona evaluate upstream fetch failed。预期的隐身 pending、配额及登录分支不会调用错误日志。 - 核对耗时边界:后端请求预设为 8 秒,Trace 为 6 秒;响应体读取与前端轮询总时长不能由这两个数字推导。
- 检查响应语义:画像保留上游状态,即使载荷回退为
{};Trace 的可用标志与国家字段完整性应分别检查。
本地代码没有自动重试或退避策略。重复请求会重复访问上游;是否由页面禁用重复点击、限制并发或停止轮询,未在本次源码中确认。
安全扩展边界
- 若扩展画像信号,应同步确认上游 observation 协议、枚举以及前端解释方式;薄代理不会替代这些契约验证。
- 若增加国家或字段校验,应将其视为接口行为变化;当前实现仅检查真值。
- 若统一两个代理的请求头清理,应考虑评估对调用者上下文的依赖,不能不加区分地删除所有头。
- 若增加缓存,应先识别访问者隔离要求;画像是逐访问者评估,不能因相同国家参数而共享报告。
- 若增加重试,应避免对画像拒绝、隐身配额耗尽或需要登录的响应进行无条件重试。
以上是依据现有边界提出的维护建议,不是仓库已经实现的机制。
测试证据与验证范围
源码检索发现 persona-server-probe.test.js 导入 probeTrace、parseTraceBody 与地理位置探针,但本页没有读取其断言,不能据此声明具体覆盖范围。画像处理器的注释提到方法防御分支有测试,实际测试断言同样未在本次读取范围内;本次任务也未运行测试。
建议回归时优先检查:ID 类型与长度、404 到 pending 的转换、429/401/403 分支、画像空或非法 JSON 响应、缺失基址、Trace 缺少 loc、超时与并发调用隔离。这些是根据已验证分支列出的验证建议,不代表已有自动化测试清单。
相关链接
- 隐身检测的业务状态转换:理解轮询与错误的界限。
- 身份画像的上游代理契约:确认本地和评价服务的责任划分。
- 网络出口的独立观测:理解国家与协议证据的来源。
- 共享请求的取消机制:排查超时与调用者取消的传播。