AI 应用免费额度防滥用的分层方案
文章分析邮箱验证、绑卡和 IP 限流各自的局限,提出先限制最大成本,再叠加滥用识别。核心措施是在模型调用前,由服务端检查全局每日预算与单账户每日额度。
AI 深度解读
AI 应用的免费额度,每被刷掉一次,都会变成真实的模型账单。作者给出的办法是分层治理:先限制最大损失,再降低批量注册的收益,最后把验证和绑卡留给高风险账号。
这套思路值得看,因为它把成本控制和注册体验放进了同一个方案。不过,原文没有提供拦截率、误伤率或转化率对照,而且作者也在推广自家的 FindIP Shield。可以借鉴架构,但产品效果还需要自己验证。
先封住账单,再讨论怎么识别刷子
作者把全站免费用量的每日预算排在第一位,再加上单账号每日上限。全站额度耗尽后,免费请求进入队列,或者等到第二天。即使识别系统漏掉攻击者,一晚上的损失也有边界。
预算检查必须发生在服务端、模型调用之前。
下面这段代码用 Redis 累加预估 token,同时检查全站和账号额度:
// 为免费用户调用模型之前
async function canSpendFree(userId: string, estTokens: number) {
const today = new Date().toISOString().slice(0, 10);
const [global, user] = await Promise.all([
redis.incrby(`free:global:${today}`, estTokens),
redis.incrby(`free:user:${userId}:${today}`, estTokens),
]);
await redis.expire(`free:global:${today}`, 172800);
await redis.expire(`free:user:${userId}:${today}`, 172800);
return global <= GLOBAL_FREE_DAILY && user <= USER_FREE_DAILY;
}
要看清 redis.incrby 这一行:它先增加计数,再判断是否超额,所以被拒绝的请求也会占额度。estTokens 还是预估值,原文没有展示模型完成后的实际用量结算。这更适合作为预算预占示意,不能直接当成完整计费系统;172800 是键的过期秒数,日期则按 UTC 划分。
下一层是降低每个账号的收益。作者举的例子是「每天 50,持续一周」比「立即给 350」更难套利,因为批量账号需要持续维护。新账号先用便宜模型,验证邮箱并积累正常使用记录后,再解锁更好的模型。
这里的关键是调整激励:让真实用户有机会体验,让刷号者付出等待和维护成本。至于正常用户是否「几乎无感」,原文没有给数据,取决于产品的一次有效体验究竟需要多少额度。
邮箱只能清理低成本滥用,关联账号才更关键
邮箱验证挡不住临时邮箱,免费版强制绑卡又可能损失注册。仅按 IP 限流,也容易把同一个 NAT 后面的办公室用户一起拦住。
作者建议先做便宜的邮箱清理:去掉首尾空格、转小写,把 googlemail.com 归并到 gmail.com,移除 +tag,并去掉 Gmail 用户名里的点;再用 disposable-email-domains 这类列表识别临时邮箱。规范化结果建立唯一索引,删除账号后保留其哈希,防止删号重注册重新领取额度。
但这段规则有适用边界。原文对所有邮箱都删除 +tag,不能默认所有邮件服务都采用相同语义。落地时应按邮箱提供商确认规则,避免把不同地址误合并。
接下来才是关联账号。单个 VPN 或共享 IP 不能证明作弊,要看多个信号是否同时出现:
- 网络属于住宅、移动运营商,还是 VPN 出口和云服务器;原文举例,一小时内来自后者的 10 个账号更值得关注。
- 注册过程中,IP、ASN 或国家是否突然变化。
- 前一个账号耗尽额度后,是否几分钟内就出现新账号。
- 新账号的首批提示词、文件或项目结构,是否与旧账号高度相似。
大白话解释,就是把账号放回连续行为里看:谁在什么时候注册,做了什么,又接着出现了谁。作者强调从第一天记录这些信息,因为没留下的历史无法事后聚类。不过,原文没有提供相似度算法、阈值或验证结果。
风险服务提供信号,最终决定仍在后端
作者的产品 FindIP Shield 负责网络信号,给访问和表单提交打出 0 到 100 的评分,并提供 VPN、proxy、Tor、hosting、datacenter、恶意 IP 和会话内 IP 变化等原因。高风险提交可以触发短暂等待、Turnstile 或停止提示,这些干预默认关闭。
浏览器通过脚本 https://cdn.findip.net/shield/v1.js 接入,参数包括 data-site-key="pub_xxxxxxxxx"、data-auto-track="true"、data-privacy-mode="balanced"。注册时用 FindIP.getSession().sessionId 取会话 ID,随请求传给后端。
下面的服务端代码说明了风险结果如何转成业务动作:
async function shieldVerdict(sessionId: string) {
const res = await fetch('https://shield.findip.net/v1/shield/sessions/verify', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SHIELD_SECRET_KEY}`, // sec_...,仅服务端使用
'Content-Type': 'application/json',
},
body: JSON.stringify({ session_id: sessionId, event: 'signup_attempt' }),
});
const v = await res.json();
// 未知不等于安全:可能没有会话,或缺少对应的 IP 情报。
if (!v.verified || v.risk_status === 'unknown') return 'monitor';
if (v.recommendation === 'block') return 'require_card';
if (v.recommendation === 'challenge') return v.challenge_passed ? 'allow' : 'require_verification';
return 'allow';
}
要注意 block 对应的是 require_card,仍给用户留下证明自己的路径;unknown 则进入 monitor,没有直接封禁。前端判断可以被绕过,所以密钥和最终决策都留在服务端。
Shield 不读取表单值,也不做设备指纹,因此无法单独识别换了浏览器配置的同一台电脑。它每天每站免费提供 10,000 events,另有 WordPress plugin、Shopify app、GTM template 和无代码 Cloudflare 集成。
原文要求把它与邮箱、时间和工作负载信号组合,但提到的第 5 步决策表并未实际给出,具体阈值仍需团队自行定义。
我的判断
我赞同先控制支出,再逐步增加识别能力。相比只做邮箱验证,它能覆盖已经通过注册的滥用;相比所有人先绑卡,它保留了低门槛体验。
但「不会伤害注册」仍是目标,尚无对照数据支持。额度拆分、便宜模型和风险验证都可能影响正常用户,尤其是需要一次完成复杂任务的产品。住宅网络也不能自动判为安全,VPN 更不能直接判为作弊。
这是一套值得试行的分层设计,效果仍属于作者的经验判断。
自家产品的披露是透明的,但不能用产品介绍替代评测。真正需要观察的是免费成本、有效体验、验证完成情况与误伤反馈,而不是单看拦下了多少注册。
能拿来做什么
- 给 Node 模型调用入口加全站和单账号预算检查,并补齐预估额度的预占、实际用量结算及失败释放。
- 如果产品免费额度被集中消耗,可以试行按日发放和便宜模型起步,同时观察用户能否完成第一次有效任务。
- 在注册后端记录网络类型、会话变化和额度耗尽时间,先观察账号关联,再决定哪些组合信号触发额外验证。
- 前端负责传递会话 ID、展示等待或验证状态;后端结合业务信号决定放行、验证或绑卡,避免仅凭 VPN 标签封禁。