Comment détecter les utilisateurs VPN dans votre application avec un seul appel API
Ajoutez la détection VPN, proxy et Tor aux flux d'inscription, de paiement et de connexion. Middleware express, intégration Next.js et exemples de notation des risques avec du code fonctionnel.
Votre page de paiement reçoit 50 commandes de la même plage IP en 12 heures, toutes utilisant des cartes prépayées. Votre essai gratuit affiche 200 inscriptions à partir des adresses IP du centre de données en une semaine. Votre point de terminaison de connexion voit tentatives de bourrage d’informations d’identification à partir d’adresses IP proxy en rotation.
Vous devez savoir quand le trafic provient d’un nœud de sortie VPN, proxy ou Tor. Ne pas le bloquer purement et simplement, mais pour ajuster votre score de risque. Un appel API vous donne ce signal.
L'appel API
Envoyer l'adresse IP de l'utilisateur à POST /v1/vpn-detect. Aucune dépendance, aucun SDK requis.
curl -X POST https://api.botoi.com/v1/vpn-detect \\
-H "Content-Type: application/json" \\
-d '{"ip": "185.220.101.1"}'
Réponse pour un nœud de sortie Tor connu (score de risque 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
}
}
}
Réponse pour une IP résidentielle propre (score de risque 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
}
}
}
La réponse vous donne cinq indicateurs booléens (is_vpn, is_proxy,
is_tor, is_datacenter) plus un chiffre risk_score
de 0 à 100. Les connexions Tor obtiennent un score de 90. Les adresses IP du centre de données obtiennent un score de 60. Noms d'hôtes suspects
score 40. Les adresses IP résidentielles propres obtiennent un score de 0.
Intégration 1 : middleware express
Ce middleware appelle l'API de détection VPN et attache le résultat à req.vpnRisk.
Chaque gestionnaire de route en aval peut vérifier req.vpnRisk.isVpn prendre des décisions
sans répéter l'appel 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
// }
// });
Le middleware ne s'ouvre pas. Si l'API est inaccessible ou expire après 3 secondes, elle définit
tous les drapeaux à false et laisse la demande continuer. Vos utilisateurs ne voient jamais d'erreur
causé par une panne d’un tiers.
Intégration 2 : protection contre le paiement Next.js
La fraude au paiement suit un schéma : nouveau compte, carte prépayée, connexion VPN. Ce Next.js Le gestionnaire d’itinéraire App Router vérifie les trois signaux et achemine les commandes suspectes vers le manuel examinez-les au lieu de les approuver automatiquement.
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(),
});
}
Le gestionnaire ne rejette pas la commande. Il renvoie un 202 avec un "nous confirmerons dans les 30 minutes" message. Cela donne à votre équipe le temps d'examiner sans avertir un mauvais acteur qu'elle a été. signalé. Les clients légitimes utilisant les VPN voient toujours leur commande traitée après un court délai.
Intégration 3 : Limitation du taux de connexion
Les attaques de credential stuffing proviennent souvent d’adresses IP VPN ou proxy pour éviter le blocage basé sur l’adresse IP. Appliquez des limites de débit plus strictes à ces connexions : 3 tentatives de connexion toutes les 15 minutes pour les utilisateurs VPN contre 10 pour les utilisateurs réguliers.
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);
Le rapport 3:10 ralentit les attaques automatisées des IP VPN sans affecter la plupart des utilisateurs réels. Un utilisateur légitime d'un VPN d'entreprise qui fait une erreur de saisie de son mot de passe à trois reprises obtient toujours un un message d'erreur clair et une fenêtre de nouvelle tentative, pas une interdiction permanente.
Construire un score de risque composé
La détection VPN à elle seule constitue un faible signal de fraude. De nombreux utilisateurs légitimes utilisent des VPN. Le signal reçoit plus fort lorsque vous le combinez avec d’autres contrôles. Appelez trois points de terminaison botoi en parallèle :
/v1/vpn-detectpour le type de connexion (VPN, proxy, Tor, datacenter)/v1/disposable-email/checkpour la qualité des e-mails (jetable vs permanent)/v1/ip/lookupen cas de non-concordance géographique (pays IP par rapport au pays de facturation)
Cette fonction appelle les trois et produit un score de risque de 0 à 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); }
Les trois appels d'API s'exécutent en parallèle avec Promise.all, donc la latence totale est égale
l'appel le plus lent (généralement moins de 100 ms). Les pondérations de notation sont un point de départ. Accordez-les
sur la base de vos données de fraude. Si les e-mails jetables constituent votre principale source de rétrofacturations, augmentez
ce poids. Si les inadéquations géographiques sont rares et généralement bénignes pour votre base d’utilisateurs, réduisez-les.
Quand NE PAS bloquer les utilisateurs VPN
Le blocage dur du trafic VPN est une erreur pour la plupart des applications. Voici les raisons courantes les gens se connectent via un VPN :
- Politique d'entreprise. Les entreprises acheminent par défaut le trafic des employés via un VPN. Le blocage de ces connexions signifie que vos clients B2B ne peuvent pas utiliser votre produit pendant les heures de travail.
- Confidentialité. Les utilisateurs soucieux de leur confidentialité exécutent des VPN sur chaque connexion comme référence mesure de sécurité. Ce sont des clients payants, pas des fraudeurs.
- Accès Internet restreint. Les utilisateurs de certains pays comptent sur les VPN pour atteindre votre produit du tout. Le blocage des VPN les verrouille complètement.
- Wi-Fi public. Toute personne connecté à un café ou à un réseau d'aéroport devrait utiliser un VPN. Les pénaliser pour une bonne hygiène de sécurité crée une mauvaise incitation.
- Journalistes et chercheurs. Les personnes occupant ces rôles utilisent Tor et les VPN pour protection des sources et sécurité opérationnelle. Les bloquer peut avoir des conséquences démesurées.
La bonne approche : signaler et marquer, ne pas bloquer durement. Utilisez la détection VPN comme entrée dans un fonction de risque qui prend en compte plusieurs signaux. Acheminez les transactions à haut risque vers une file d’attente de révision. Laissez les humains prendre la décision finale dans les cas ambigus.
Points clés
-
POST /v1/vpn-detectretoursis_vpn,is_proxy,is_tor,is_datacenter, etrisk_scorepour n’importe quelle IP. - Aucune clé API requise pour les tests (5 requêtes par minute). Les clés gratuites débloquent des limites plus élevées pour la production.
- Attachez la vérification VPN au middleware Express afin que chaque itinéraire ait accès aux données de risque sans répéter l'appel.
- Combinez la détection VPN avec des vérifications de courrier électronique jetables et la géolocalisation IP pour un complexe un score de risque suffisamment élevé pour agir.
- Marquez les connexions VPN pour examen. Ne les bloquez pas. Les utilisateurs légitimes utilisent des VPN pour confidentialité, politique d’entreprise et accès Internet restreint.
FAQ
- Comment détecter les utilisateurs VPN dans mon application ?
- Envoyez l'adresse IP de l'utilisateur dans une requête POST au point de terminaison botoi /v1/vpn-detect. La réponse inclut des indicateurs booléens pour is_vpn, is_proxy, is_tor et is_datacenter, plus un risque_score de 0 à 100. Appelez ce point de terminaison lors de l’inscription, de la connexion ou du paiement pour signaler les connexions des services d’anonymisation.
- Quelle API de détection VPN est la meilleure pour les applications de production ?
- Recherchez une API qui renvoie des indicateurs distincts pour les connexions VPN, proxy, Tor et du centre de données plutôt qu'un seul booléen. Le point de terminaison botoi /v1/vpn-detect renvoie les quatre indicateurs plus un score de risque numérique, et il fonctionne sans clé API à 5 requêtes par minute. Pour les charges de travail de production, les clés API débloquent des limites de débit plus élevées à partir du niveau gratuit.
- Dois-je bloquer tous les utilisateurs VPN de mon application ?
- Non. De nombreux utilisateurs légitimes utilisent des VPN pour des raisons de confidentialité, pour des raisons de politique d'entreprise ou parce qu'ils vivent dans des régions où l'accès à Internet est restreint. Le blocage de tout le trafic VPN exclut les clients payants. Utilisez plutôt la détection VPN comme signal dans un score de risque composé et signalez les connexions suspectes pour examen.
- Puis-je détecter les connexions proxy et Tor avec le même appel API ?
- Oui. Le point de terminaison botoi /v1/vpn-detect renvoie des indicateurs booléens distincts pour is_vpn, is_proxy et is_tor dans une seule réponse. Vous n'avez pas besoin d'appels API distincts pour chaque type de connexion. Le point de terminaison renvoie également is_datacenter pour identifier le trafic provenant de fournisseurs de cloud comme AWS ou Google Cloud.
- Comment combiner la détection VPN avec d’autres signaux de fraude ?
- Appelez plusieurs points de terminaison botoi en parallèle : /v1/vpn-detect pour le type de connexion, /v1/disposable-email/check pour la qualité du courrier électronique et /v1/ip/lookup pour la non-concordance géographique entre le pays IP et le pays de facturation. Pondérez chaque signal et additionnez-les dans un score de risque de 0 à 100. Les scores supérieurs à 70 sont soumis à une révision manuelle ; les scores inférieurs à 30 sont transmis.
Commencez a construire avec botoi
150+ endpoints API pour la recherche, le traitement de texte, la generation d'images et les utilitaires pour developpeurs. Offre gratuite, sans carte bancaire.