VPN 和代理检测 API:标记滥用而不阻止用户
通过一个 POST 请求检测 VPN、代理、Tor 和数据中心连接。 包括 Next.js 中间件、Express 速率限制和欺诈评分示例。
您的 SaaS 应用程序为每位用户提供免费试用。 用户注册、激活 VPN,然后再次注册。 滥用促销会损失您的收入并扭曲您的转化指标。 您需要标记 VPN 和代理 注册时的连接。
本指南涵盖了 botoi POST /v1/vpn-detect 端点:它返回什么,如何返回
将其集成到 Next.js 和 Express 应用程序中,如何将其与其他欺诈信号相结合,以及在哪里
它达不到要求。
终点
正文中包含 IP 地址的一个 POST 请求。 没有特殊标头,不需要 API 密钥 匿名访问。
curl -X POST https://api.botoi.com/v1/vpn-detect \\
-H "Content-Type: application/json" \\
-d '{ "ip": "185.220.101.1" }'
对已知 Tor 出口节点的响应:
{
"success": true,
"data": {
"ip": "185.220.101.1",
"is_vpn": true,
"is_proxy": false,
"is_tor": true,
"is_datacenter": false,
"provider": null,
"risk_score": 90,
"checks": {
"tor": true,
"datacenter": false,
"suspicious_hostname": false
}
}
}
对干净住宅IP的响应:
{
"success": true,
"data": {
"ip": "73.162.45.118",
"is_vpn": false,
"is_proxy": false,
"is_tor": false,
"is_datacenter": false,
"provider": null,
"risk_score": 0,
"checks": {
"tor": false,
"datacenter": false,
"suspicious_hostname": false
}
}
}
响应字段
- 是VPN (布尔值):如果 IP 属于已知数据中心范围或具有包含 VPN 相关关键字的可疑反向 DNS 主机名,则为 True。
- 是代理 (布尔值):如果反向 DNS 主机名建议代理服务器,则为 true。
- is_tor (布尔值):如果 IP 与已知的 Tor 出口节点匹配,则为 True。
- 是_数据中心 (布尔值):如果 IP 属于 AWS、Google Cloud、Azure、DigitalOcean 或 Linode CIDR 范围,则为 True。
- 提供者 (字符串或空):云提供商名称
is_datacenter是真的。 对于住宅和 Tor IP 为空。 - 风险评分 (数字,0-100):Tor 连接得分 90,数据中心 IP 得分 60,可疑主机名得分 40。干净的住宅 IP 得分 0。
- 检查 (object):触发了哪些检测方法的故障,用于调试。
使用 Next.js 中间件在注册时标记 VPN 用户
该中间件拦截对您的注册路由的 POST 请求,根据调用者的 IP 检查调用者的 IP VPN 检测 API,并在检测到 VPN 时将标头附加到请求中。 您的注册管理员 读取标题并决定做什么:要求电子邮件验证、添加手动审核标志,或者 缩短试用期。
import { NextRequest, NextResponse } from 'next/server';
const VPN_DETECT_URL = 'https://api.botoi.com/v1/vpn-detect';
export async function middleware(req: NextRequest) {
if (req.method !== 'POST') {
return NextResponse.next();
}
const ip = req.headers.get('x-forwarded-for')?.split(',')[0]?.trim()
|| req.ip
|| '127.0.0.1';
try {
const res = await fetch(VPN_DETECT_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ip }),
signal: AbortSignal.timeout(3000),
});
const { data } = await res.json();
if (data.is_vpn || data.is_tor || data.is_proxy) {
// Add a header so your signup handler can flag the account
const response = NextResponse.next();
response.headers.set('x-vpn-detected', 'true');
response.headers.set('x-vpn-risk-score', String(data.risk_score));
return response;
}
} catch {
// Fail open: if the API is unreachable, let the request through
console.warn('VPN detection check failed, allowing request');
}
return NextResponse.next();
}
export const config = {
matcher: ['/api/auth/signup', '/api/auth/register'],
};
中间件不会阻止注册。 它将信号传递给您的处理程序。 这是正确的 方法,因为 VPN 流量并不能证明欺诈。 使用公司 VPN 或旅行的用户 通过限制性网络是合法客户。
Express 中 VPN IP 的更严格的速率限制
如果您运行 API,则可以对 VPN 和代理连接应用更严格的速率限制,而不会阻塞 他们直接。 该中间件为标准用户每小时提供 100 个请求,为 VPN 用户提供 20 个请求。
import type { Request, Response, NextFunction } from 'express';
const VPN_DETECT_URL = 'https://api.botoi.com/v1/vpn-detect';
const BOTOI_API_KEY = process.env.BOTOI_API_KEY;
// Standard limits
const STANDARD_LIMIT = 100; // requests per hour
const VPN_LIMIT = 20; // requests per hour for VPN users
const requestCounts = new Map<string, { count: number; resetAt: number }>();
export async function vpnAwareRateLimit(
req: Request,
res: Response,
next: NextFunction
) {
const ip = req.headers['x-forwarded-for']?.toString().split(',')[0]?.trim()
|| req.socket.remoteAddress
|| 'unknown';
let isVpn = false;
try {
const vpnRes = await fetch(VPN_DETECT_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': \`Bearer \${BOTOI_API_KEY}\`,
},
body: JSON.stringify({ ip }),
signal: AbortSignal.timeout(2000),
});
const { data } = await vpnRes.json();
isVpn = data.is_vpn || data.is_tor || data.is_proxy;
} catch {
// Fail open: use standard limits if detection fails
}
const limit = isVpn ? VPN_LIMIT : STANDARD_LIMIT;
const now = Date.now();
const entry = requestCounts.get(ip);
if (!entry || entry.resetAt < now) {
requestCounts.set(ip, { count: 1, resetAt: now + 3600_000 });
res.setHeader('X-RateLimit-Limit', limit);
return next();
}
entry.count++;
if (entry.count > limit) {
return res.status(429).json({
error: 'Rate limit exceeded',
retryAfter: Math.ceil((entry.resetAt - now) / 1000),
});
}
res.setHeader('X-RateLimit-Limit', limit);
res.setHeader('X-RateLimit-Remaining', limit - entry.count);
next();
}
标准限制和 VPN 限制之间的 5:1 比例是一个起点。 根据您的滥用情况进行调整 模式。 如果您的 API 处理付款或帐户修改,则更严格的 VPN 限制是有意义的。 对于只读端点,您可能根本不需要差异限制。
欺诈评分:将 VPN 检测与其他信号相结合
VPN 检测本身就是一个微弱的欺诈信号。 VPN + 一次性电子邮件 + 新帐户 + 失败 付款尝试是一个强烈的信号。 该功能将多个输入组合成一个欺诈行为 得分。
interface FraudSignals {
vpnDetected: boolean;
riskScore: number;
disposableEmail: boolean;
accountAge: number; // days
failedPayments: number;
}
function calculateFraudScore(signals: FraudSignals): number {
let score = 0;
// VPN/proxy risk contributes up to 30 points
if (signals.vpnDetected) {
score += Math.round(signals.riskScore * 0.3);
}
// Disposable email: strong signal
if (signals.disposableEmail) {
score += 35;
}
// New account + VPN is a red flag
if (signals.accountAge < 1 && signals.vpnDetected) {
score += 20;
}
// Failed payment history
score += Math.min(signals.failedPayments * 10, 30);
return Math.min(score, 100);
}
async function assessSignupRisk(ip: string, email: string) {
const [vpnRes, emailRes] = await Promise.all([
fetch('https://api.botoi.com/v1/vpn-detect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ip }),
}),
fetch('https://api.botoi.com/v1/disposable-email/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email }),
}),
]);
const vpnData = (await vpnRes.json()).data;
const emailData = (await emailRes.json()).data;
const fraudScore = calculateFraudScore({
vpnDetected: vpnData.is_vpn || vpnData.is_tor,
riskScore: vpnData.risk_score,
disposableEmail: emailData.is_disposable,
accountAge: 0, // new signup
failedPayments: 0,
});
return {
fraudScore,
action: fraudScore > 70 ? 'block' : fraudScore > 40 ? 'review' : 'allow',
details: {
vpn: vpnData.is_vpn,
tor: vpnData.is_tor,
proxy: vpnData.is_proxy,
disposableEmail: emailData.is_disposable,
},
};
}
两个 API 调用并行运行,因此总延迟是两者中较慢的一个(通常低于
100 毫秒)。 这 action 字段为您提供了三个级别:允许、审查或阻止。 对于分数
在 40 到 70 之间,将注册路由到手动审核队列,而不是自动拒绝它。
缓存以减少 API 调用
IP 地址不会在每次请求时更改其 VPN 状态。 将结果缓存10分钟 减少 API 使用,而不会错过状态更改。
const vpnCache = new Map<string, { result: VpnResult; expiresAt: number }>();
const CACHE_TTL = 10 * 60 * 1000; // 10 minutes
interface VpnResult {
isVpn: boolean;
isTor: boolean;
isProxy: boolean;
riskScore: number;
}
async function checkVpn(ip: string): Promise<VpnResult> {
const cached = vpnCache.get(ip);
if (cached && cached.expiresAt > Date.now()) {
return cached.result;
}
try {
const res = await fetch('https://api.botoi.com/v1/vpn-detect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ip }),
signal: AbortSignal.timeout(3000),
});
const { data } = await res.json();
const result: VpnResult = {
isVpn: data.is_vpn,
isTor: data.is_tor,
isProxy: data.is_proxy,
riskScore: data.risk_score,
};
vpnCache.set(ip, { result, expiresAt: Date.now() + CACHE_TTL });
return result;
} catch {
return { isVpn: false, isTor: false, isProxy: false, riskScore: 0 };
}
}
对于多服务器部署,请将内存中的映射替换为 Redis 或 Upstash。 相同的缓存键 (IP 地址)和 TTL 模式适用。
此 API 无法检测到的内容
对局限性的诚实比华丽的推销更重要。 这里是VPN检测的地方 达不到要求。
- 住宅 VPN。 iCloud Private Relay 等服务和一些配置 的 Mulvad 通过住宅 IP 地址路由流量。 这些 IP 看起来相同 常规家庭互联网连接。 反向 DNS 或基于 CIDR 的检测无法捕获它们。
-
移动运营商 NAT。 移动运营商使用运营商级 NAT,这意味着
数以千计的用户共享一个 IP 地址。 标记这些 IP 会影响合法用户。
API 可能会返回
is_datacenter: false和risk_score: 0对于这些 即使 VPN 用户在 IP 后面也是如此。 - 私有 SOCKS5 代理。 代理托管在住宅的个人服务器上 ISP 不会出现在数据中心 CIDR 范围中,并且没有可疑的主机名。 他们是 自动检测不可见。
- 仅 IPv6 连接。 当前端点仅支持 IPv4 地址。 IPv6 VPN 检测不可用。
-
共享主机上的误报。 一名开发人员正在运行一个业余项目
DigitalOcean 水滴将触发
is_datacenter: true。 这并不意味着他们是 隐藏他们的身份。
该检测非常适合商业 VPN 提供商(NordVPN、ExpressVPN、Surfshark、CyberGhost) 和 Tor 流量。 它捕获大多数数据中心托管的代理。 它无法捕获住宅 VPN 或 私人代理。 预计常见情况的检测率为 80-90%,边缘情况的检测率接近于零。
标记,不要阻止
对于大多数应用程序来说,彻底阻止 VPN 用户是一个错误。 原因如下:
- 注重隐私且无意滥用您的服务的用户默认运行 VPN。
- 使用公司 VPN 的员工通过公司基础设施路由所有流量。
- 互联网受到限制的国家/地区的用户完全依赖 VPN 来访问您的产品。
- 记者、活动家和安全研究人员出于合法原因使用 Tor。
正确的方法:向帐户添加风险标记,需要额外验证(电子邮件 确认、电话号码、付款方式),或将注册路由到审核队列。 让人类 对模棱两可的案件做出最后决定。
要点
-
POST /v1/vpn-detect回报is_vpn,is_proxy,is_tor,is_datacenter,provider, 和risk_score对于任何 IPv4 地址。 - 匿名访问不需要 API 密钥(每分钟 5 个请求)。 免费钥匙解锁 500 个 每天的请求数。
- 检测涵盖商业 VPN 提供商、已知的 Tor 出口节点和主要云提供商 IP 范围。 住宅 VPN 和私人代理可以通过。
- 将 VPN 检测与一次性电子邮件检查、帐户年龄和付款历史记录相结合 有意义的欺诈分数。 仅 VPN 状态不足以采取行动。
- 标记 VPN 连接以供审查。 不要阻止他们。 合法用户为了隐私而运行 VPN, 公司政策以及限制区域的访问。
FAQ
- 如何在我的应用程序中检测 VPN 用户?
- 将用户的 IP 地址发送到 VPN 检测 API(例如 POST /v1/vpn-detect)并检查响应中的 is_vpn 布尔值。 该 API 还返回 is_proxy、is_tor 和 is_datacenter 标志,以便您可以区分不同的匿名方法。 在注册、登录或结帐期间调用此端点以标记可疑连接以供审查。
- VPN 检测 API 能否捕获每个 VPN 连接?
- 不会。VPN 检测的工作原理是检查已知的数据中心 IP 范围、反向 DNS 主机名和 Tor 退出节点列表。 住宅 VPN 服务通过家庭 ISP 地址路由流量,这看起来与常规住宅流量相同。 商业 VPN(NordVPN、ExpressVPN、Surfshark)的检测率预计为 80-90%,但住宅 VPN 和私人代理的检测率较低。
- 我应该阻止所有 VPN 用户访问我的应用程序吗?
- 不会。许多合法用户出于隐私、公司政策或因为居住在互联网访问受限的地区而运行 VPN。 彻底阻止 VPN 流量将导致付费客户无法使用。 相反,将 VPN 连接标记为较高风险,并将其与其他信号(电子邮件域、支付方式、帐户期限)结合起来做出决定。
- VPN、代理和 Tor 检测之间有什么区别?
- VPN 对通过隧道到服务器的所有流量进行加密,服务器通常由商业提供商运行。 代理通过中间服务器路由流量,无需完全加密。 Tor 通过多个志愿者操作的中继路由流量,以实现最大程度的匿名性。 botoi API 为每种类型返回单独的布尔标志,因此您可以对每种类型应用不同的策略。
- botoi VPN 检测 API 是否需要 API 密钥?
- 不需要。匿名访问允许每分钟 5 个请求,无需 API 密钥。 对于生产工作负载,免费的 API 密钥可将限制提高到每天 500 个请求。 付费计划起价为 9 美元/月,费率限额更高。
开始使用 botoi 构建
150+ 个 API 端点,涵盖查询、文本处理、图片生成和开发者工具。免费套餐,无需信用卡。