Frontend Labs
⋯

Day 103 — IP Reputation & ASN Classification

Learning objective Classify IP addresses as datacenter, residential, or known malicious
Key packages net, strings
Security context 60–70% of bot traffic originates from datacenter IPs; classification gives strong first-pass signal

Key Concepts

ASN classification: Each ASN (Autonomous System Number) maps to an organization. AWS = ASN 16509, GCP = ASN 15169, etc. IPs in datacenter ASNs are more likely bots.

In production: Use MaxMind GeoIP2 or ipinfo.io. For this exercise: hardcode representative CIDR ranges for major cloud providers.

Exercise — IP Classifier

package bot

import (
    "net"
)

type IPCategory string

const (
    IPCategoryDatacenter  IPCategory = "datacenter"
    IPCategoryHosting     IPCategory = "hosting"
    IPCategoryResidential IPCategory = "residential"
    IPCategoryUnknown     IPCategory = "unknown"
)

type cidrEntry struct {
    network  *net.IPNet
    provider string
    category IPCategory
}

// Representative CIDR ranges for major cloud providers
// In production: use a regularly updated database
var cloudCIDRs = []struct {
    cidr     string
    provider string
}{
    // AWS
    {"3.0.0.0/8", "AWS"},
    {"13.32.0.0/11", "AWS CloudFront"},
    {"52.0.0.0/8", "AWS"},
    {"54.0.0.0/8", "AWS"},
    // GCP
    {"8.34.208.0/20", "GCP"},
    {"34.0.0.0/9", "GCP"},
    {"35.184.0.0/13", "GCP"},
    // Azure
    {"13.64.0.0/11", "Azure"},
    {"20.0.0.0/8", "Azure"},
    {"40.64.0.0/10", "Azure"},
    // DigitalOcean
    {"104.16.0.0/12", "DigitalOcean"},
    {"159.89.0.0/16", "DigitalOcean"},
    // Linode/Akamai
    {"45.33.0.0/17", "Linode"},
    {"139.162.0.0/16", "Linode"},
    // Hetzner
    {"5.9.0.0/16", "Hetzner"},
    {"78.46.0.0/15", "Hetzner"},
}

var compiledCIDRs []cidrEntry

func init() {
    for _, c := range cloudCIDRs {
        _, network, err := net.ParseCIDR(c.cidr)
        if err != nil {
            continue
        }
        compiledCIDRs = append(compiledCIDRs, cidrEntry{
            network:  network,
            provider: c.provider,
            category: IPCategoryDatacenter,
        })
    }
}

func classifyIP(ipStr string) (IPCategory, string) {
    ip := net.ParseIP(ipStr)
    if ip == nil {
        return IPCategoryUnknown, ""
    }

    for _, entry := range compiledCIDRs {
        if entry.network.Contains(ip) {
            return entry.category, entry.provider
        }
    }

    // IPv6-only from unusual ASN — modest signal
    if ip.To4() == nil {
        return IPCategoryUnknown, "ipv6"
    }

    return IPCategoryResidential, ""
}

// IPReputationDetector implements Detector
type IPReputationDetector struct{}

func (d *IPReputationDetector) Detect(r *http.Request, session *SessionScore) {
    ip := clientIP(r)
    category, provider := classifyIP(ip)

    switch category {
    case IPCategoryDatacenter:
        session.AddSignal(Signal{
            "datacenter_ip", 15, CategoryNetwork,
            fmt.Sprintf("IP belongs to %s (datacenter)", provider),
        })
    case IPCategoryHosting:
        session.AddSignal(Signal{
            "hosting_ip", 20, CategoryNetwork,
            fmt.Sprintf("IP belongs to %s (hosting)", provider),
        })
    }
}

Generated from week14.md · 8 Oct 2026 16:36