All dispatches/Security

Deterministic VPN & Residential Proxy Detection with Explainable Reason Codes

Why black-box fraud scores fail in production, and how ipscope combines WireGuard/OpenVPN protocol probes, MTU/MSS telemetry, and ASN classification to emit auditable reason codes.

Why opaque IP risk scores fail in production engineering

If you have ever maintained a login, signup, or payment checkout pipeline, you have likely experienced the failure mode of black-box IP reputation APIs. A third-party vendor returns a single opaque number like "fraud_score": 88, your middleware blocks the request, and twenty minutes later a high-value enterprise customer or mobile subscriber opens an urgent support ticket because they were locked out of their account.

When your security engineers inspect the logs, "fraud_score": 88 tells them nothing actionable:

  • Was the IP address an active Tor exit node or a high-velocity credential-stuffing botnet?
  • Was it a Fortune 500 employee routing traffic through a corporate cloud security gateway (Zscaler, Cloudflare Gateway, Netskope) hosted on a datacenter ASN?
  • Was it an iPhone user in Safari with Apple iCloud Private Relay enabled?
  • Or was it simply a shared 5G cellular CGNAT gateway where one bad actor out of 4,000 subscribers triggered a static blocklist three days ago?

When an IP intelligence provider conflates network access topology, anonymizer protocol state, and recent abuse velocity into a single undocumented integer, you cannot write surgical security policies. You are forced to choose between high false-positive rates that hurt conversion and lax thresholds that let automated abuse through.

In ipscope, we designed our security architecture around a strict separation of concerns:

  1. Access network topology (connection): Classifies whether the IP belongs to a residential, mobile, business, or hosting network.
  2. Structural anonymizer state (security): Identifies active VPNs, residential/datacenter proxies, Tor exits, and consumer privacy relays, backed by deterministic reason_codes.
  3. Time-decayed hostile activity (security_extensions): Tracks active scanning and brute-force cadence via abuse_velocity_score (0 to 100) and observation_recency_hours.

Anatomy of the ipscope security and connection blocks

Every lookup licensed for the anonymity and connection families returns explicit boolean flags, an enumerated anonymizer_type, a calibrated confidence score (0.0 to 1.0), and an array of machine-parsable reason_codes:

{
  "ip": "198.51.100.42",
  "ip_version": 4,
  "matched_network": "198.51.100.0/24",
  "connection": {
    "connection_type": "hosting",
    "is_residential": false,
    "is_mobile": false,
    "is_business": false,
    "is_hosting": true
  },
  "security": {
    "is_vpn": true,
    "is_hosting": true,
    "is_tor_exit": false,
    "is_proxy": false,
    "anonymizer_type": "vpn",
    "confidence": 0.98,
    "reason_codes": [
      "vpn_handshake_confirmed",
      "commercial_vpn_exit_enumerated",
      "datacenter_asn_range"
    ]
  },
  "security_extensions": {
    "abuse_velocity_score": 14,
    "is_known_scanner": false,
    "observation_recency_hours": 6
  }
}

Understanding anonymizer_type values

anonymizer_typeDescriptionTypical Risk Profile
"none"Direct residential, mobile, business, or un-tunneled cloud connectionBaseline traffic (inspect is_hosting and abuse_velocity_score for bot activity)
"vpn"Commercial VPN service or self-hosted WireGuard / OpenVPN / IPsec exit nodeMedium risk: common among privacy-conscious users and geo-hoppers; step up to MFA/Passkey on sensitive mutations
"proxy"Open HTTP/SOCKS4/SOCKS5 proxy or residential proxy exit nodeHigh risk: heavily used by credential-stuffing frameworks, sneaker bots, and ad-fraud farms
"tor"Active Tor network exit relay verified against hourly consensusHigh risk for account creation and payment checkout; legitimate for anonymous reading
"private_relay"Consumer OS/browser privacy relay (such as Apple iCloud Private Relay)Low fraud risk: hardware-attested consumer devices masking client IP within the user's real country/region

How ipscope detects VPNs, proxies, and relays across four signal layers

Layer 1: Active cryptographic protocol handshakes (vpn_handshake_confirmed)

Many commercial VPN providers lease ordinary datacenter /24 blocks from cloud and bare-metal hosts without registering the blocks under the VPN brand's name. Passive WHOIS analysis only sees the underlying hosting company (for example, M247, Datacamp, Choopa, or Hetzner).

To identify active VPN concentrators deterministically, our distributed scanner fleet performs non-intrusive, protocol-specific handshake verification across the IPv4 and active IPv6 address space:

  • OpenVPN (UDP/TCP): Sends a valid OpenVPN P_CONTROL_HARD_RESET_CLIENT_V2 control packet and verifies whether the listener responds with a P_CONTROL_HARD_RESET_SERVER_V2 session state machine reply or requires tls-auth / tls-crypt HMAC framing characteristic of OpenVPN daemons.
  • IKEv2 / IPsec (UDP 500 / UDP 4500): Sends a standard IKE_SA_INIT proposal and inspects the responder's cryptographic transform selection, vendor ID payloads (VID), and NAT-Traversal encapsulation headers.
  • WireGuard and Shadowsocks / V2Ray / Trojan: Combines TLS ALPN fingerprint telemetry, port allocation entropy, and continuous exit-node enumeration across 95+ commercial VPN providers. When a user connects through a multi-hop VPN where the ingress IP differs from the egress IP, our continuous egress enumeration probes record the exact exit /32 and attach commercial_vpn_exit_enumerated.

Layer 2: Residential proxy exit detection (residential_proxy_exit)

Detecting datacenter VPNs is only half the battle. Modern scraping and credential-stuffing operations route requests through residential proxy networks (often powered by SDKs embedded in free mobile/desktop apps, residential bandwidth-sharing containers, or compromised home routers). Because a residential proxy exit resides on a legitimate consumer ISP (connection.is_residential: true, connection.is_hosting: false), ASN-only checks completely miss it.

Our compiler identifies residential proxy exits using three orthogonal signals:

  1. Reverse-Tunnel Beacon & Relay Node Correlation: Residential proxy networks require persistent control connections from the home device back to central proxy aggregator gateways. By tracking the aggregator infrastructure and enumerating proxy exit pools, we identify active residential proxy IPs while they are participating in the relay mesh.
  2. Multi-Tenant Session & TLS/TCP Fingerprint Divergence: A genuine household router typically exhibits a small, stable set of OS TCP stack parameters and TLS client fingerprints (JA4) over a 1-hour window. A residential proxy exit multiplexing thousands of automated bot requests exhibits rapid, simultaneous rotation of incompatible OS/TLS signatures and anomalous destination ASN fan-out within minutes.
  3. Ephemeral Port Exhaustion & Darknet Hit Correlation: When residential IPs suddenly participate in coordinated distributed scans or credential-stuffing waves observed across our global darknet sensor network, residential_proxy_exit and abuse_velocity_score elevate in tandem.

Layer 3: Hourly Tor directory consensus and exit list synchronization (tor_consensus_exit)

The Tor network consists of guard, middle, and exit relays. Only exit relays (Exit flag in the directory consensus, plus actual outbound exit addresses discovered via TorDNSEL / exit-addresses descriptors) originate traffic toward public web servers.

Our ingestion pipeline syncs directly with the Tor directory authorities every 60 minutes. We distinguish between middle/guard relays (which do not egress user HTTP traffic) and active exit IPs, setting is_tor_exit: true, anonymizer_type: "tor", and reason_codes: ["tor_consensus_exit"].

Layer 4: Consumer privacy relay separation (private_relay)

One of the most common sources of false positives in legacy GeoIP databases is treating Apple iCloud Private Relay (and similar hardware-attested consumer oblivious HTTP relays) as hostile anonymous proxies.

Unlike commercial VPNs that allow users to spoof arbitrary foreign countries, consumer privacy relays enforce strict regional locality (preserving the user's true country and typically their metro/time zone) and require hardware-backed device attestation to enter the relay ingress. Moreover, several major privacy relay operators publish official RFC 8805 egress feeds so web services can localize content properly.

By classifying these egress blocks with anonymizer_type: "private_relay" and reason_codes: ["consumer_privacy_relay"], ipscope lets your security team allow legitimate iOS and macOS users through checkout without friction while still enforcing strict controls on commercial VPNs and SOCKS proxies.

Reference dictionary of security.reason_codes

Every flag in the security block is backed by one or more deterministic reason_codes so your engineering and support teams can audit every decision in your logs:

Reason CodeTrigger ConditionPrimary Associated Flag
vpn_handshake_confirmedActive OpenVPN, IKEv2/IPsec, or WireGuard protocol listener verified on hostis_vpn: true
commercial_vpn_exit_enumeratedVerified outbound exit IP of a commercial VPN provideris_vpn: true
datacenter_asn_rangePrefix belongs to a dedicated cloud, VPS, or colocation hosting ASNis_hosting: true
tor_consensus_exitActive Tor exit relay in the current hourly directory consensusis_tor_exit: true
open_proxy_listenerActive HTTP CONNECT, SOCKS4, or SOCKS5 proxy verified on hostis_proxy: true
residential_proxy_exitResidential or mobile IP actively routing traffic for a proxy relay poolis_proxy: true
consumer_privacy_relayVerified egress IP of an attested consumer privacy relay (e.g. iCloud Private Relay)anonymizer_type: "private_relay"
rdns_vpn_tokenReverse DNS (PTR) or operator WHOIS explicitly designates VPN exit infrastructureis_vpn: true

Building an explainable authentication & checkout policy in TypeScript

Here is a complete, production-ready policy engine using @ipscope/sdk-node that handles consumer privacy relays, shared mobile CGNAT gateways, commercial VPNs, residential proxies, and Tor exits with full audit logging:

import { IpScopeClient, type IpLookupResponse } from '@ipscope/sdk-node';

const client = new IpScopeClient({
  apiKey: process.env.IPSCOPE_KEY!,
  maxCacheEntries: 8192,
  cacheTtlMs: 60_000,
});

export type PolicyAction = 'allow' | 'step_up_mfa' | 'challenge_captcha' | 'block';

export interface SecurityPolicyDecision {
  action: PolicyAction;
  ruleId: string;
  reasonCodes: string[];
  anonymizerType: string;
  abuseVelocity: number;
}

export async function evaluateCheckoutRequest(
  clientIp: string,
  userCountryBillingIso2: string,
): Promise<SecurityPolicyDecision> {
  const res: IpLookupResponse = await client.lookupIp(clientIp);
  const { geo, connection, security, security_extensions } = res;
  const velocity = security_extensions?.abuse_velocity_score ?? 0;

  // Rule 1: Block active Tor exits and high-velocity scanners on payment checkout
  if (security.is_tor_exit || velocity >= 80 || security_extensions?.is_known_scanner) {
    return {
      action: 'block',
      ruleId: 'HIGH_RISK_TOR_OR_SCANNER',
      reasonCodes: security.reason_codes,
      anonymizerType: security.anonymizer_type,
      abuseVelocity: velocity,
    };
  }

  // Rule 2: Allow attested consumer privacy relays without friction if country matches
  if (security.anonymizer_type === 'private_relay') {
    const countryMatches = geo.physical_country_code === userCountryBillingIso2;
    return {
      action: countryMatches ? 'allow' : 'step_up_mfa',
      ruleId: countryMatches ? 'ALLOW_PRIVATE_RELAY_LOCAL' : 'STEP_UP_PRIVATE_RELAY_FOREIGN',
      reasonCodes: security.reason_codes,
      anonymizerType: security.anonymizer_type,
      abuseVelocity: velocity,
    };
  }

  // Rule 3: Challenge residential or open proxies (high botnet / credential stuffing correlation)
  if (security.is_proxy) {
    return {
      action: velocity >= 40 ? 'block' : 'challenge_captcha',
      ruleId: 'PROXY_DETECTED',
      reasonCodes: security.reason_codes,
      anonymizerType: security.anonymizer_type,
      abuseVelocity: velocity,
    };
  }

  // Rule 4: Step up to Passkey / WebAuthn / 2FA for commercial VPNs or direct datacenter egress
  if (security.is_vpn || (security.is_hosting && !connection?.is_business)) {
    return {
      action: 'step_up_mfa',
      ruleId: 'VPN_OR_HOSTING_EGRESS',
      reasonCodes: security.reason_codes,
      anonymizerType: security.anonymizer_type,
      abuseVelocity: velocity,
    };
  }

  // Rule 5: Clean residential, mobile, or business network
  return {
    action: 'allow',
    ruleId: 'CLEAN_ACCESS_NETWORK',
    reasonCodes: security.reason_codes,
    anonymizerType: security.anonymizer_type,
    abuseVelocity: velocity,
  };
}

Handling residential proxy churn and dynamic ISP reassignment

Unlike datacenter /24 blocks where VPN servers stay online for months, residential IPv4 addresses are reassigned via DHCP when home routers reboot or ISP PPPoE sessions cycle. If a residential IP was part of a proxy pool on Tuesday, and the ISP reassigns that IP to a different household on Thursday, permanently flagging the IP as is_proxy: true would penalize an innocent subscriber.

To prevent stale residential proxy flags, our compiler enforces a strict observation TTL and confidence decay curve on all residential and mobile access networks (connection.is_residential || connection.is_mobile):

  • Datacenter VPN and hosting classifications (connection.is_hosting: true) are anchored to stable subnet and protocol listener state verified across consecutive sweeps.
  • Residential and mobile proxy classifications (residential_proxy_exit) require recurring telemetry confirmation within a rolling 24-to-72-hour window. Once proxy relay activity ceases on a residential /32, its proxy confidence decays automatically and the IP returns to a clean residential state on the next daily compilation cycle.

Frequently asked questions (FAQ)

Why are both is_vpn: true and is_hosting: true set on most commercial VPN IPs?

Almost all commercial VPN providers (Mullvad, NordVPN, ExpressVPN, ProtonVPN, IVPN) rent bare-metal or virtual servers inside commercial data centers (connection_type: "hosting"). Therefore, the IP address is simultaneously located inside a hosting provider's ASN (is_hosting: true) and operating as a verified VPN exit node (is_vpn: true). Keeping both booleans independent allows you to distinguish a raw cloud server (is_hosting: true, is_vpn: false) from a commercial VPN exit (is_hosting: true, is_vpn: true) and a residential VPN/proxy (is_hosting: false, is_vpn: true).

Should I block all is_vpn: true traffic at the firewall level?

Rarely. Approximately 15% to 25% of technical users, remote software engineers, and privacy-conscious consumers keep a commercial VPN or corporate split-tunnel active during normal browsing. Blocking all VPNs at Layer 3/4 turns away legitimate users. Instead, allow read-only browsing and normal authenticated sessions on VPNs, and use security.is_vpn to trigger step-up authentication (such as Passkeys, 2FA, or email verification) on high-risk actions like account registration, password resets, API key creation, and payment checkout.

Are reason_codes and anonymizer_type available in the offline .mmdb and .parquet files?

Yes. Every database SKU that includes the anonymity family (IPScope-Privacy and IPScope-Complete) contains the full security map, including is_vpn, is_hosting, is_tor_exit, is_proxy, anonymizer_type, confidence, and the reason_codes list.

Start with 30K free lookups a month.

No card, no trial clock. Upgrade when your traffic does, or download the database when you would rather keep it in-house.