Pular para o conteúdo
Integration

Como detectar usuários VPN em seu aplicativo com uma chamada de API

| 7 min read

Adicione detecção de VPN, proxy e Tor aos fluxos de inscrição, checkout e login. Middleware expresso, integração Next.js e exemplos de pontuação de risco com código funcional.

Lock icon on a digital network background representing VPN security
Photo by Towfiqu barbhuiya on Unsplash

Sua página de checkout recebe 50 pedidos da mesma faixa de IP em 12 horas, todos usando cartões pré-pagos. Sua avaliação gratuita mostra 200 inscrições de IPs de datacenter em uma semana. Seu endpoint de login vê tentativas de preenchimento de credenciais de IPs de proxy rotativos.

Você precisa saber quando o tráfego vem de um nó de saída VPN, proxy ou Tor. Para não bloquear imediatamente, mas para ajustar sua pontuação de risco. Uma chamada de API fornece esse sinal.

A chamada da API

Envie o IP do usuário para POST /v1/vpn-detect. Sem dependências, sem necessidade de SDK.

curl -X POST https://api.botoi.com/v1/vpn-detect \\
  -H "Content-Type: application/json" \\
  -d '{"ip": "185.220.101.1"}'

Resposta para um nó de saída Tor conhecido (pontuação de risco 90):

{
  "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
    }
  }
}

Resposta para um IP residencial limpo (pontuação de risco 0):

{
  "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
    }
  }
}

A resposta fornece cinco sinalizadores booleanos (is_vpn, is_proxy, is_tor, is_datacenter) mais um número risk_score de 0 a 100. Conexões Tor pontuam 90. IPs de datacenter pontuam 60. Nomes de host suspeitos pontuação 40. IPs residenciais limpos pontuam 0.

Integração 1: Middleware expresso

Este middleware chama a API de detecção de VPN e anexa o resultado a req.vpnRisk. Todo manipulador de rota downstream pode verificar req.vpnRisk.isVpn para tomar decisões sem repetir a chamada da API.

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;

interface VpnRisk {
  isVpn: boolean;
  isProxy: boolean;
  isTor: boolean;
  isDatacenter: boolean;
  riskScore: number;
}

declare global {
  namespace Express {
    interface Request {
      vpnRisk?: VpnRisk;
    }
  }
}

export async function vpnDetectMiddleware(
  req: Request,
  _res: Response,
  next: NextFunction
) {
  const ip = req.headers['x-forwarded-for']?.toString().split(',')[0]?.trim()
    || req.socket.remoteAddress
    || 'unknown';

  try {
    const res = await fetch(VPN_DETECT_URL, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': \`Bearer \${BOTOI_API_KEY}\`,
      },
      body: JSON.stringify({ ip }),
      signal: AbortSignal.timeout(3000),
    });

    const { data } = await res.json();

    req.vpnRisk = {
      isVpn: data.is_vpn,
      isProxy: data.is_proxy,
      isTor: data.is_tor,
      isDatacenter: data.is_datacenter,
      riskScore: data.risk_score,
    };
  } catch {
    // Fail open: if detection fails, treat as clean
    req.vpnRisk = {
      isVpn: false,
      isProxy: false,
      isTor: false,
      isDatacenter: false,
      riskScore: 0,
    };
  }

  next();
}

// Usage in your route:
// app.use(vpnDetectMiddleware);
//
// app.post('/api/signup', (req, res) => {
//   if (req.vpnRisk?.isVpn) {
//     // require email verification or flag for review
//   }
// });

O middleware falha ao abrir. Se a API estiver inacessível ou expirar após 3 segundos, ela definirá todas as bandeiras para false e permite que a solicitação continue. Seus usuários nunca veem um erro causado por uma interrupção de terceiros.

Integração 2: proteção de checkout Next.js

A fraude no checkout segue um padrão: nova conta, cartão pré-pago, conexão VPN. Este Next.js O manipulador de rota do App Router verifica todos os três sinais e encaminha pedidos suspeitos para manual revisar em vez de aprová-los automaticamente.

import { NextRequest, NextResponse } from 'next/server';

const VPN_DETECT_URL = 'https://api.botoi.com/v1/vpn-detect';
const BOTOI_API_KEY = process.env.BOTOI_API_KEY!;

interface CheckoutBody {
  cardType: 'credit' | 'debit' | 'prepaid';
  accountCreatedAt: string;
  amount: number;
  currency: string;
}

export async function POST(req: NextRequest) {
  const body: CheckoutBody = await req.json();

  const ip = req.headers.get('x-forwarded-for')?.split(',')[0]?.trim()
    || '127.0.0.1';

  // Check VPN status
  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(3000),
  });

  const { data: vpnData } = await vpnRes.json();

  const accountAge = Date.now() - new Date(body.accountCreatedAt).getTime();
  const isNewAccount = accountAge < 24 * 60 * 60 * 1000; // less than 24 hours
  const isPrepaid = body.cardType === 'prepaid';
  const isVpn = vpnData.is_vpn || vpnData.is_tor || vpnData.is_proxy;

  // VPN + new account + prepaid card = manual review
  if (isVpn && isNewAccount && isPrepaid) {
    return NextResponse.json({
      status: 'review',
      orderId: crypto.randomUUID(),
      message: 'Order placed. We will confirm within 30 minutes.',
    }, { status: 202 });
  }

  // VPN + new account (no prepaid) = proceed with logging
  if (isVpn && isNewAccount) {
    console.log(JSON.stringify({
      event: 'checkout_vpn_new_account',
      ip,
      riskScore: vpnData.risk_score,
      amount: body.amount,
    }));
  }

  // Process the order normally
  return NextResponse.json({
    status: 'approved',
    orderId: crypto.randomUUID(),
  });
}

O manipulador não rejeita o pedido. Ele retorna um 202 com "confirmaremos em 30 minutos" mensagem. Isso dá à sua equipe tempo para revisar sem avisar um mau ator de que eles foram sinalizado. Clientes legítimos em VPNs ainda terão seus pedidos processados ​​após um pequeno atraso.

Integração 3: Limitação de taxa de login

Os ataques de preenchimento de credenciais geralmente vêm de VPN ou IPs proxy para evitar o bloqueio baseado em IP. Aplique limites de taxa mais rígidos a essas conexões: 3 tentativas de login a cada 15 minutos para usuários VPN versus 10 para usuários regulares.

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;

// VPN users: 3 attempts per 15 minutes
// Regular users: 10 attempts per 15 minutes
const VPN_LOGIN_LIMIT = 3;
const STANDARD_LOGIN_LIMIT = 10;
const WINDOW_MS = 15 * 60 * 1000;

const loginAttempts = new Map<string, { count: number; resetAt: number }>();

export async function loginRateLimit(
  req: Request,
  res: Response,
  next: NextFunction
) {
  const ip = req.headers['x-forwarded-for']?.toString().split(',')[0]?.trim()
    || req.socket.remoteAddress
    || 'unknown';

  // Check VPN status
  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
  }

  const limit = isVpn ? VPN_LOGIN_LIMIT : STANDARD_LOGIN_LIMIT;
  const now = Date.now();
  const entry = loginAttempts.get(ip);

  if (!entry || entry.resetAt < now) {
    loginAttempts.set(ip, { count: 1, resetAt: now + WINDOW_MS });
    return next();
  }

  entry.count++;

  if (entry.count > limit) {
    const retryAfter = Math.ceil((entry.resetAt - now) / 1000);
    return res.status(429).json({
      error: 'Too many login attempts. Try again later.',
      retryAfter,
    });
  }

  next();
}

// Mount on your login route:
// app.post('/api/auth/login', loginRateLimit, loginHandler);

A proporção de 3:10 retarda ataques automatizados de IPs VPN sem afetar a maioria dos usuários reais. Um usuário legítimo em uma VPN corporativa que digita sua senha incorretamente três vezes ainda recebe uma mensagem de erro clara e uma janela de nova tentativa, não um banimento permanente.

Construindo uma pontuação de risco composta

A detecção de VPN por si só é um sinal fraco de fraude. Muitos usuários legítimos executam VPNs. O sinal fica mais forte quando você o combina com outros testes. Chame três terminais botoi em paralelo:

  • /v1/vpn-detect para tipo de conexão (VPN, proxy, Tor, datacenter)
  • /v1/disposable-email/check para qualidade de e-mail (descartável vs permanente)
  • /v1/ip/lookup para incompatibilidade geográfica (país de IP x país de cobrança)

Esta função chama todos os três e produz uma pontuação de risco de 0 a 100:

interface RiskResult {
  score: number;
  action: 'allow' | 'review' | 'block';
  signals: {
    vpn: boolean;
    tor: boolean;
    proxy: boolean;
    vpnRiskScore: number;
    disposableEmail: boolean;
    geoMismatch: boolean;
    ipCountry: string;
    billingCountry: string;
  };
}

async function calculateRisk(
  ip: string,
  email: string,
  billingCountry: string
): Promise<RiskResult> {
  const BOTOI_KEY = process.env.BOTOI_API_KEY!;
  const headers = {
    'Content-Type': 'application/json',
    'Authorization': \`Bearer \${BOTOI_KEY}\`,
  };

  // Run all three checks in parallel
  const [vpnRes, emailRes, geoRes] = await Promise.all([
    fetch('https://api.botoi.com/v1/vpn-detect', {
      method: 'POST',
      headers,
      body: JSON.stringify({ ip }),
    }),
    fetch('https://api.botoi.com/v1/disposable-email/check', {
      method: 'POST',
      headers,
      body: JSON.stringify({ email }),
    }),
    fetch('https://api.botoi.com/v1/ip/lookup', {
      method: 'POST',
      headers,
      body: JSON.stringify({ ip }),
    }),
  ]);

  const vpnData = (await vpnRes.json()).data;
  const emailData = (await emailRes.json()).data;
  const geoData = (await geoRes.json()).data;

  const ipCountry = geoData.country_code || '';
  const geoMismatch = ipCountry !== '' && billingCountry !== ''
    && ipCountry.toUpperCase() !== billingCountry.toUpperCase();

  // Weight each signal
  let score = 0;

  // VPN/proxy/Tor: up to 30 points
  if (vpnData.is_vpn || vpnData.is_tor || vpnData.is_proxy) {
    score += Math.round(vpnData.risk_score * 0.3);
  }

  // Disposable email: 25 points
  if (emailData.is_disposable) {
    score += 25;
  }

  // Geo mismatch (IP country != billing country): 20 points
  if (geoMismatch) {
    score += 20;
  }

  // VPN + disposable email combo: extra 15 points
  if ((vpnData.is_vpn || vpnData.is_tor) && emailData.is_disposable) {
    score += 15;
  }

  score = Math.min(score, 100);

  return {
    score,
    action: score > 70 ? 'block' : score > 30 ? 'review' : 'allow',
    signals: {
      vpn: vpnData.is_vpn,
      tor: vpnData.is_tor,
      proxy: vpnData.is_proxy,
      vpnRiskScore: vpnData.risk_score,
      disposableEmail: emailData.is_disposable,
      geoMismatch,
      ipCountry,
      billingCountry,
    },
  };
}

// Example usage:
// const risk = await calculateRisk('185.220.101.1', 'user@tempmail.com', 'US');
// if (risk.action === 'review') { queueForManualReview(orderId); }
// if (risk.action === 'block') { rejectTransaction(orderId); }

Todas as três chamadas de API são executadas em paralelo com Promise.all, então a latência total é igual a chamada mais lenta (normalmente abaixo de 100 ms). Os pesos de pontuação são um ponto de partida. Sintonize-os com base em seus dados de fraude. Se e-mails descartáveis são sua maior fonte de estornos, aumente esse peso. Se as incompatibilidades geográficas forem raras e geralmente benignas para sua base de usuários, diminua-as.

Quando NÃO bloquear usuários VPN

O bloqueio rígido do tráfego VPN é um erro para a maioria dos aplicativos. Aqui estão os motivos comuns as pessoas se conectam por meio de uma VPN:

  • Política corporativa. As empresas encaminham o tráfego de funcionários por meio de uma VPN por padrão. Bloquear essas conexões significa que seus clientes B2B não poderão usar seu produto durante o horário de trabalho.
  • Privacidade. Usuários preocupados com a privacidade executam VPNs em todas as conexões como base medida de segurança. Eles são clientes pagantes, não fraudadores.
  • Acesso restrito à Internet. Os usuários em determinados países dependem de VPNs para acessar seu produto. O bloqueio de VPNs as bloqueia completamente.
  • Wi-Fi público. Qualquer pessoa em uma rede de cafeterias ou aeroportos deveria usar um VPN. Penalizá-los por uma boa higiene de segurança cria o incentivo errado.
  • Jornalistas e pesquisadores. Pessoas nessas funções usam Tor e VPNs para proteção de fontes e segurança operacional. Bloqueá-los pode ter consequências descomunais.

A abordagem correta: sinalizar e marcar, não bloquear fortemente. Use a detecção de VPN como uma entrada para um função de risco que considera múltiplos sinais. Encaminhe transações de alto risco para uma fila de revisão. Deixe os humanos tomarem a decisão final em casos ambíguos.

Pontos-chave

  • POST /v1/vpn-detect retorna is_vpn, is_proxy, is_tor, is_datacenter, e risk_score para qualquer IP.
  • Nenhuma chave de API necessária para teste (5 solicitações por minuto). Chaves gratuitas desbloqueiam limites mais altos para produção.
  • Anexe a verificação VPN ao middleware Express para que cada rota tenha acesso aos dados de risco sem repetir a chamada.
  • Combine a detecção de VPN com verificações de e-mail descartáveis e geolocalização de IP para um composto pontuação de risco que seja forte o suficiente para agir.
  • Sinalize conexões VPN para revisão. Não os bloqueie. Usuários legítimos executam VPNs para privacidade, política corporativa e acesso restrito à Internet.

FAQ

Como posso detectar usuários VPN em meu aplicativo?
Envie o endereço IP do usuário em uma solicitação POST para o endpoint botoi /v1/vpn-detect. A resposta inclui sinalizadores booleanos para is_vpn, is_proxy, is_tor e is_datacenter, além de uma pontuação de risco de 0 a 100. Chame esse endpoint durante a inscrição, login ou checkout para sinalizar conexões de serviços anônimos.
Qual API de detecção de VPN é melhor para aplicativos de produção?
Procure uma API que retorne sinalizadores separados para conexões VPN, proxy, Tor e datacenter, em vez de um único booleano. O endpoint botoi /v1/vpn-detect retorna todos os quatro sinalizadores mais uma pontuação de risco numérica e funciona sem chave de API a 5 solicitações por minuto. Para cargas de trabalho de produção, as chaves de API desbloqueiam limites de taxas mais altos começando no nível gratuito.
Devo bloquear todos os usuários VPN do meu aplicativo?
Não. Muitos usuários legítimos utilizam VPNs por questões de privacidade, políticas corporativas ou porque vivem em regiões com acesso restrito à Internet. O bloqueio de todo o tráfego VPN bloqueia os clientes pagantes. Em vez disso, use a detecção de VPN como um sinal em uma pontuação de risco composta e sinalize conexões suspeitas para análise.
Posso detectar conexões proxy e Tor com a mesma chamada de API?
Sim. O endpoint botoi /v1/vpn-detect retorna sinalizadores booleanos separados para is_vpn, is_proxy e is_tor em uma única resposta. Você não precisa de chamadas de API separadas para cada tipo de conexão. O endpoint também retorna is_datacenter para identificar o tráfego de provedores de nuvem como AWS ou Google Cloud.
Como posso combinar a detecção de VPN com outros sinais de fraude?
Chame vários endpoints botoi em paralelo: /v1/vpn-detect para tipo de conexão, /v1/disposable-email/check para qualidade de e-mail e /v1/ip/lookup para incompatibilidade geográfica entre o país de IP e o país de cobrança. Pese cada sinal e some-os em uma pontuação de risco de 0 a 100. Pontuações acima de 70 vão para revisão manual; pontuações abaixo de 30 passam.

Comece a construir com botoi

150+ endpoints de API para consultas, processamento de texto, geração de imagens e utilitários para desenvolvedores. Plano gratuito, sem cartão de crédito.