ipasis
Blog/Engineering

How to Architect a Custom IP Risk Scoring System

January 12, 20265 min read

Binary IP blacklisting is a crude instrument. In modern fraud prevention, blocking an IP address simply because it belongs to a data center often results in high false-positive rates (blocking legitimate VPN users) or false negatives (missing residential proxies).

To effectively mitigate fraud, security engineers must build probabilistic risk scoring systems that aggregate multiple signals into a normalized risk score (0-100). This guide outlines the architecture for building such a system from scratch.

Core Architecture Components

A robust IP scoring engine consists of four distinct stages:

  1. Ingestion & Enrichment: Fast lookup of metadata (ASN, Geo, Connection Type).
  2. Velocity Tracking: Temporal analysis of the IP's behavior.
  3. Normalization: Converting disparate signals into a unified scale.
  4. Aggregation: The weighted algorithm that produces the final score.

1. Signal Acquisition

Your system needs raw data. Building this from scratch implies aggregating threat feeds (e.g., Spamhaus, FireHOL) and ASN databases. You are looking for three specific signal types:

  • Reputation: Is the IP on a known blocklist?
  • Identity: Is the ASN associated with hosting providers (DigitalOcean, AWS) or residential ISPs (Comcast, Verizon)?
  • Anonymizers: Is the IP a Tor exit node, open proxy, or VPN endpoint?

2. Implementing Velocity Tracking (The Time Dimension)

Static data isn't enough. A residential IP behaving like a bot (100 requests/minute) is high risk. You must implement a sliding window counter. Redis is the standard tool here due to its atomic increment operations and TTL features.

Node.js Example: Velocity Check with Redis

const Redis = require('ioredis');
const redis = new Redis();

async function getVelocityScore(ip) {
  const key = `velocity:${ip}`;
  const windowSeconds = 60;
  
  // Atomic increment
  const currentCount = await redis.incr(key);
  
  // Set expiry on first request
  if (currentCount === 1) {
    await redis.expire(key, windowSeconds);
  }

  // Linear mapping: >50 req/min = Max Risk
  const threshold = 50;
  if (currentCount > threshold) return 1.0;
  return currentCount / threshold;
}

3. The Scoring Algorithm

Once signals are collected, they must be weighted. A linear combination model is usually sufficient for V1, though advanced systems eventually migrate to Random Forest classifiers.

Python Example: Weighted Risk Calculator

from dataclasses import dataclass

@dataclass
class IPSignals:
    is_hosting: bool
    is_tor: bool
    velocity_score: float # 0.0 to 1.0
    geo_mismatch: bool

class RiskEngine:
    # Configuration Weights
    WEIGHTS = {
        'hosting': 20,
        'tor': 100,         # Immediate red flag
        'velocity': 40,     # Dynamic behavior
        'geo_mismatch': 15
    }

    def calculate_risk(self, signals: IPSignals) -> int:
        score = 0
        
        if signals.is_tor:
            return 100 # Short circuit for critical threats
            
        if signals.is_hosting:
            score += self.WEIGHTS['hosting']
            
        if signals.geo_mismatch:
            score += self.WEIGHTS['geo_mismatch']
            
        # Add normalized velocity score
        score += (signals.velocity_score * self.WEIGHTS['velocity'])
        
        # Clamp score to 0-100 range
        return min(100, int(score))

4. Normalization Strategies

One common pitfall is improper scaling. If your velocity metric returns values from 0 to 10,000, it will drown out your boolean flags (VPN=True/False).

Always normalize continuous variables to a 0.0 - 1.0 float before applying weights. For non-linear distributions (like request counts), consider using a Sigmoid function rather than linear clamping to dampen the impact of extreme outliers while remaining sensitive to the threshold between "normal" and "suspicious."

FAQ

Q: How do we handle latency requirements?
A: Risk scoring is usually in the hot path of authentication or payment processing. The entire lookup, including Redis calls and logic, must execute in under 50ms. If you are querying external APIs during the scoring process, ensure you set aggressive timeouts (e.g., 200ms) and have a "fail-open" or "fail-closed" default strategy.

Q: What is an acceptable False Positive Rate?
A: For login protection, shoot for <0.1%. For payment fraud, you can be more aggressive (<1%). Always implement a "challenge" flow (MFA/CAPTCHA) for medium-risk scores (e.g., 50-75) rather than a hard block.

Q: How often should threat feeds be updated?
A: Daily is insufficient. Proxy networks rotate IPs hourly. Your ingestion pipeline needs to update mostly-static ASN data weekly, but threat lists should be updated continuously.

Build vs. Buy

Building a scoring engine gives you control over the algorithm, but the heavy lifting lies in maintaining the data sources. IP detection is a moving target; residential proxies rotate millions of IPs daily, rendering static lists obsolete within hours.

If you need an enterprise-grade risk score without managing the infrastructure of threat intelligence feeds, integrate the IPASIS API. We handle the signal aggregation, proxy detection, and velocity tracking so you can focus on your core application logic.

Get your API Key

Start detecting VPNs and Bots today.

Identify anonymized traffic instantly with IPASIS.

Get API Key