不可能旅行的误报:VPN、时间与位置数据
不可能旅行检测会将一次登录中基于 IP 推算出的位置,与该用户此前存储的位置点进行比较,并标记出在已经过的时间内,物理上不可能达到的推算速度。一个 high 结果意味着原始的距离/时间计算超过了某个阈值——它本身并不能证明账户已被盗用,一些完全正常的情形同样可能触发它。
评分背后的计算方式
POST /v1/risk/check 会计算用户此前存储位置与当前位置之间的大圆距离,除以自上一次观测以来经过的时间,并对推算出的速度进行分级:低于 500 公里/小时为 low,500 至 900 公里/小时之间为 medium,超过 900 公里/小时为 high(impossibleTravel: true)。一次真实的长途航班,其速度可能恰好落在 medium/high 的边界附近,这正是这些阈值被设计为区间、而不是单一分界线的原因——但这项计算本身并没有“航班”“VPN”或“共享网络”的概念,它只看到两个点和一段时长。
首次观测永远不会被标记
某个用户的首次检查没有任何可供比较的记录:它始终返回 risk: "low",previousLocation 为 null,distance/speed/elapsed 相关数值也均为 null,并且(除非 commit: false)会成为新的存储基线。这意味着来自新设备或新网络的第一次登录,默认情况下会直接建立信任——如果攻击者的第一步操作出于同样的结构性原因同样不会被标记,这一点值得了解。
常见触发误报的情形
在两次登录之间切换 VPN 出口节点或移动网络网关,可能会让解析出的位置发生大幅变化,而实际上完全没有发生真实的位移——v1 目前没有针对 VPN 或数据中心的抑制机制,因此这种情况读起来与真正的不可能旅行毫无区别。普通的移动运营商 IP 波动(同一个物理位置,在不同请求中解析到不同的网关城市)以及共享/任播 IP 地址段,也可能产生同样的效果。这些情形都与攻击者无关,它们只是 IP 推算位置这一机制本身的副产物,准确度一文对此有更深入的说明。
一个 high 结果并不能证明什么
risk: "high" 是一个来自距离/时间计算的原始信号——它不是经过验证的账户盗用事件,不能说明谁发起了这次登录,也不能说明用户的凭证是否真的已经泄露。请将它当作您自己审查流程或分步验证决策中的一项输入,并结合您已经掌握的身份验证证据一并考虑,而不是仅凭它单独做出判定。
一个双观测示例
某用户存储的基线位置是多伦多,观测时间为 UTC 09:00。一次登录来自解析为法兰克福的 IP,时间为 UTC 09:45——45 分钟内约 6,400 公里,推算速度远超 900 公里/小时,因此返回 risk: "high" 且 impossibleTravel: true。这个结果既可能对应一个账户被盗用、并从另一个大洲被使用的情况,也可能对应同一个合法用户的流量,出于某种无关原因突然经由法兰克福的 VPN 出口路由——无论是哪种情况,API 返回的分类结果都是一样的,这正是它被设计为审查流程中的一项输入、而不是自动拦截依据的原因。
本文并未声称的内容
本文描述的是 Watch-IP 自身的 v1 实现。它并不是在声称能够全面检测账户盗用、进行行为分析或设备指纹识别——这些都是该端点未曾尝试的不同技术。该端点的可用性以及独立的方案系列强制执行机制尚未得到确认;在生产环境中依赖它之前,请先联系我们。
参考来源
由 Watch-IP 编辑团队撰写(2026-09-06)。已参照 apps/api/src/lib/risk.ts 与 apps/api/src/durable-objects/session-risk.ts 完成审校。