惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Microsoft Azure Blog
Microsoft Azure Blog
MongoDB | Blog
MongoDB | Blog
小众软件
小众软件
Apple Machine Learning Research
Apple Machine Learning Research
O
OpenAI News
酷 壳 – CoolShell
酷 壳 – CoolShell
The GitHub Blog
The GitHub Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
博客园 - 聂微东
Engineering at Meta
Engineering at Meta
W
WeLiveSecurity
Hacker News: Ask HN
Hacker News: Ask HN
大猫的无限游戏
大猫的无限游戏
Vercel News
Vercel News
D
Docker
F
Full Disclosure
AI
AI
罗磊的独立博客
博客园 - 【当耐特】
U
Unit 42
S
SegmentFault 最新的问题
Stack Overflow Blog
Stack Overflow Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
P
Palo Alto Networks Blog
博客园_首页
H
Help Net Security
量子位
月光博客
月光博客
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
F
Fortinet All Blogs
D
DataBreaches.Net
B
Blog RSS Feed
Webroot Blog
Webroot Blog
TaoSecurity Blog
TaoSecurity Blog
S
Secure Thoughts
爱范儿
爱范儿
I
InfoQ
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Attack and Defense Labs
Attack and Defense Labs
Application and Cybersecurity Blog
Application and Cybersecurity Blog
C
CERT Recently Published Vulnerability Notes
Martin Fowler
Martin Fowler
Blog — PlanetScale
Blog — PlanetScale
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
Securelist

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
ASN Lookup for Security Engineers: From Concept to Code
ABDULLAH AFZ · 2026-05-20 · via DEV Community

An ASN lookup is the fastest way to find out who actually operates the network behind an IP address. Every publicly routable IP on the internet belongs to an autonomous system, and that autonomous system number (ASN) identifies the network operator, what infrastructure they run, and who they peer with. For security engineers, this is a first-order pivot point: when an IP shows up in your logs tied to credential stuffing or port scanning, the ASN tells you whether you're looking at a residential ISP, a cloud hosting provider, or a known bulletproof hosting operation.

This tutorial covers what ASNs are, how they fit into internet routing, and then gets to the practical part: querying ASN data programmatically with curl, Python, and JavaScript so you can fold it into your investigation workflow or SIEM pipeline.

TL;DR

  • An ASN (Autonomous System Number) uniquely identifies a network that participates in internet routing via BGP.
  • As of 2025, roughly 120,000 ASNs had been allocated globally, while the number actively visible in BGP routing tables varied by dataset and vantage point.
  • ASNs classify into three types: stub (one upstream), multi-homed (multiple upstreams), and transit (carries traffic for others).
  • Security engineers use ASN data for threat attribution, network-level blocking, infrastructure mapping, and log enrichment.
  • Programmatic ASN lookups return the network operator, organization type (ISP, hosting, business, education), route prefixes, and peering relationships.
  • This tutorial includes working code for ASN lookups in curl, Python, and JavaScript using ipgeolocation.io's ASN API.

In short: IP addresses identify devices, ASNs identify the networks those devices belong to. Knowing the network behind an IP is often more actionable than knowing the IP itself.

What Is an Autonomous System (and Why Does It Have a Number)

An autonomous system (AS) is a collection of IP address ranges, called prefixes, that operate under a single routing policy managed by one organization. An ISP like Comcast, a cloud provider like AWS, and a CDN like Cloudflare each operate their own autonomous systems. Internally, an AS can run whatever routing protocols it wants (OSPF, IS-IS, EIGRP). Externally, it presents one consistent set of rules about which IP prefixes it controls and how traffic reaches them.

Each AS gets a unique number: its ASN. These are assigned by IANA through five Regional Internet Registries (RIRs): ARIN (North America), RIPE NCC (Europe, Middle East, Central Asia), APNIC (Asia-Pacific), LACNIC (Latin America), and AFRINIC (Africa). The original 16-bit format allowed 65,536 ASNs. The 32-bit format, standardized in RFC 6793, extends that to over 4.2 billion. As of 2025, roughly 120,000 ASNs had been allocated globally, while the number actively visible in BGP routing tables varied by dataset and vantage point.

Three types matter in practice:

  • Stub AS: connects to only one upstream provider. A small company with a single ISP connection. Most ASNs are stubs.
  • Multi-homed AS: connects to two or more upstreams for redundancy. A mid-size enterprise with connections to both Cogent and Lumen.
  • Transit AS: carries traffic between other autonomous systems. The backbone providers (AS3356 Lumen, AS2914 NTT, AS174 Cogent) that form the internet's core routing infrastructure. The glue that holds all of this together is BGP (Border Gateway Protocol). When an AS wants the internet to know about its IP prefixes, it announces them via BGP. Each announcement includes an AS-PATH: the sequence of ASNs that traffic traverses to reach the prefix. Routers use the AS-PATH to choose the best route, typically the shortest one.

A concrete example: your browser requests a page hosted in Frankfurt. Your ISP (say, AS7922 Comcast) checks its BGP table and finds that the shortest path to the destination prefix goes through AS3356 (Lumen) to AS24940 (Hetzner). Three ASNs, three AS-path hops.

Why Security Engineers Care About ASN Data

Threat Attribution Beyond the IP

An IP address can change hands hourly in cloud environments. An ASN identifies the network operator, which is far more stable. When you see a cluster of malicious requests coming from 5 different IPs, they might all resolve to the same ASN. That's not 5 separate actors; that's one network.

Suppose your WAF flags credential stuffing attempts from 185.220.101.x. You look up the ASN: AS205100, F3 Netze e.V., a Tor exit node operator in Germany. That single fact reframes the incident. You're not dealing with a compromised residential user; you're dealing with traffic deliberately routed through an anonymization network. The response changes from "investigate the user" to "rate-limit the network."

Tip: The type field in ASN data is your first filter. HOSTING and ISP ASNs produce most bot traffic. EDUCATION and BUSINESS ASNs are more likely to be legitimate. It's a rough heuristic, not a rule, but it cuts noise.

ASN-Based Blocking and Allowlisting

Individual IP blocking is a game of whack-a-mole. Attackers rotate IPs constantly. ASN-level blocking is coarser but more durable: if an autonomous system consistently sources malicious traffic, blocking it at the network level eliminates entire IP ranges at once.

A practical example: hosting providers like Hetzner (AS24940), OVHcloud (AS16276), and DigitalOcean (AS14061) are popular with both legitimate developers and attackers. You probably can't block them outright. But you can apply stricter rate limits and CAPTCHA challenges to traffic originating from hosting ASNs while letting residential ISP ASNs through with less friction. The ASN's type field (ISP vs HOSTING vs BUSINESS) enables this segmentation without maintaining manual IP lists.

Infrastructure Mapping with Network Topology

Basic ASN lookups tell you who operates a network. Topology data, upstream and downstream relationships, tells you how that network connects to the rest of the internet.

When investigating a phishing campaign, you find the domain resolves to an IP in AS64500 (a small hosting provider). Pulling the downstream data reveals that AS64500 has three downstream ASNs, two of which have shown up in threat feeds before. That's not a coincidence; it's infrastructure reuse across campaigns. The upstream data shows AS64500 buys transit from a single provider, which gives you an escalation path for abuse reports.

This kind of analysis is impossible with IP-level data alone. It requires the network topology layer that ASN data provides.

SIEM and Log Enrichment

Raw firewall logs are walls of IP addresses. Enriching each IP with its ASN, organization name, and network type transforms those logs from "1,000 IPs hit the login endpoint" into "380 of those IPs belong to three hosting providers, 50 belong to Tor exit ASNs, and the rest are residential ISPs." That's actionable intelligence.

Adding ASN data to your SIEM pipeline lets you aggregate by network operator instead of individual IP. When AS16276 (OVHcloud) shows up in 200 failed login events across 40 different IPs, that's one signal, not 40.

How to Query ASN Data Programmatically

What You Need

  • An API key from ipgeolocation.io
  • curl (for quick lookups), Python 3.8+, or Node.js 18+ Alternatives exist in this space. Team Cymru, HackerTarget, ipinfo, and MaxMind GeoLite2 all offer ASN data. This tutorial uses ipgeolocation.io's ASN endpoint because it returns ASN details plus routes, peers, upstreams, and downstreams in a single call, which is the part that matters for security analysis.

Pitfall: ASN data from the unified IP geolocation endpoint on most providers gives you the basic AS number and organization. If you need route prefixes and peering relationships, you need a dedicated ASN endpoint or a separate BGP data source.

curl

Set your API key as an environment variable first:

export IPGEO_API_KEY="your_api_key_here"

Enter fullscreen mode Exit fullscreen mode

The examples below pipe output to jq for readability. Drop | jq . if you don't have it installed.

The fastest way to look up an IP's ASN:

curl -s "https://api.ipgeolocation.io/v3/asn?apiKey=${IPGEO_API_KEY}&ip=8.8.8.8" | jq .

Enter fullscreen mode Exit fullscreen mode

Response:

{
  "ip": "8.8.8.8",
  "asn": {
    "as_number": "AS15169",
    "organization": "Google LLC",
    "country": "US",
    "type": "BUSINESS",
    "domain": "google.com",
    "date_allocated": "2000-03-10",
    "asn_name": "GOOGLE",
    "allocation_status": "ASSIGNED",
    "num_of_ipv4_routes": "1737",
    "num_of_ipv6_routes": "727",
    "rir": "ARIN"
  }
}

Enter fullscreen mode Exit fullscreen mode

Route counts and peering relationships change over time as BGP data evolves. The values above reflect a point-in-time snapshot.

To pull network topology for a specific ASN:

curl -s "https://api.ipgeolocation.io/v3/asn?apiKey=${IPGEO_API_KEY}&asn=AS24940&include=peers,upstreams,downstreams,routes" | jq .

Enter fullscreen mode Exit fullscreen mode

This returns the full picture: every IP prefix the AS announces, who it peers with, who provides its upstream transit, and who sits downstream. For AS24940 (Hetzner), the response includes announced IPv4 and IPv6 routes, plus the transit and peering relationships that connect Hetzner to the rest of the internet.

Python

Install the requests library if you haven't already:

pip install requests

Enter fullscreen mode Exit fullscreen mode

import os
import requests

API_KEY = os.environ.get("IPGEO_API_KEY")
if not API_KEY:
    raise RuntimeError("Missing IPGEO_API_KEY environment variable")

BASE_URL = "https://api.ipgeolocation.io/v3/asn"

def lookup_ip_asn(ip_address):
    """Resolve an IP to its ASN and network details."""
    try:
        resp = requests.get(
            BASE_URL,
            params={"apiKey": API_KEY, "ip": ip_address},
            timeout=(2.0, 5.0),
        )
        resp.raise_for_status()
        data = resp.json()

        asn_data = data.get("asn", {})
        return {
            "ip": data.get("ip"),
            "as_number": asn_data.get("as_number"),
            "organization": asn_data.get("organization"),
            "type": asn_data.get("type"),
            "country": asn_data.get("country"),
            "rir": asn_data.get("rir"),
        }
    except requests.exceptions.Timeout:
        print(f"Timeout looking up ASN for {ip_address}")
        return None
    except requests.exceptions.RequestException as e:
        print(f"ASN lookup failed for {ip_address}: {e}")
        return None


def get_asn_topology(as_number):
    """Pull full network topology for an ASN: routes, peers, upstreams, downstreams."""
    try:
        resp = requests.get(
            BASE_URL,
            params={
                "apiKey": API_KEY,
                "asn": as_number,
                "include": "routes,peers,upstreams,downstreams",
            },
            timeout=(2.0, 10.0),
        )
        resp.raise_for_status()
        return resp.json().get("asn", {})
    except requests.exceptions.RequestException as e:
        print(f"Topology lookup failed for {as_number}: {e}")
        return None


if __name__ == "__main__":
    # Basic IP-to-ASN lookup
    result = lookup_ip_asn("8.8.8.8")
    if result:
        print(f"{result['ip']} -> {result['as_number']} ({result['organization']}, {result['type']})")

    # Full topology for a hosting provider
    topology = get_asn_topology("AS24940")
    if topology:
        routes = topology.get("routes", [])
        peers = topology.get("peers", [])
        upstreams = topology.get("upstreams", [])
        downstreams = topology.get("downstreams", [])
        print(f"AS24940 announces {len(routes)} routes, peers with {len(peers)} ASNs")
        print(f"  Upstreams: {[u['as_number'] for u in upstreams]}")
        print(f"  Downstreams: {[d['as_number'] for d in downstreams]}")

Enter fullscreen mode Exit fullscreen mode

The two functions cover the two core use cases: lookup_ip_asn for enriching individual IPs in a log pipeline, and get_asn_topology for deeper investigation when you need to understand a network's structure.

A longer timeout on the topology call (10 seconds vs 5) accounts for the larger response payload when routes and peering data are included.

JavaScript (Node.js)

const API_KEY = process.env.IPGEO_API_KEY;
if (!API_KEY) {
  throw new Error("Missing IPGEO_API_KEY environment variable");
}

const BASE_URL = "https://api.ipgeolocation.io/v3/asn";

async function lookupIpAsn(ipAddress) {
  const url = `${BASE_URL}?apiKey=${API_KEY}&ip=${encodeURIComponent(ipAddress)}`;

  try {
    const resp = await fetch(url, { signal: AbortSignal.timeout(5000) });

    if (!resp.ok) {
      console.error(`ASN lookup returned ${resp.status} for ${ipAddress}`);
      return null;
    }

    const data = await resp.json();
    const asn = data.asn ?? {};

    return {
      ip: data.ip,
      asNumber: asn.as_number,
      organization: asn.organization,
      type: asn.type,
      country: asn.country,
      rir: asn.rir,
    };
  } catch (err) {
    if (err.name === "TimeoutError") {
      console.error(`Timeout looking up ASN for ${ipAddress}`);
    } else {
      console.error(`ASN lookup failed for ${ipAddress}:`, err.message);
    }
    return null;
  }
}

async function getAsnTopology(asNumber) {
  const url = `${BASE_URL}?apiKey=${API_KEY}&asn=${encodeURIComponent(asNumber)}&include=routes,peers,upstreams,downstreams`;

  try {
    const resp = await fetch(url, { signal: AbortSignal.timeout(10000) });

    if (!resp.ok) {
      console.error(`Topology lookup returned ${resp.status} for ${asNumber}`);
      return null;
    }

    const data = await resp.json();
    return data.asn ?? {};
  } catch (err) {
    console.error(`Topology lookup failed for ${asNumber}:`, err.message);
    return null;
  }
}

// Usage
const result = await lookupIpAsn("8.8.8.8");
if (result) {
  console.log(`${result.ip} -> ${result.asNumber} (${result.organization}, ${result.type})`);
}

const topo = await getAsnTopology("AS24940");
if (topo) {
  const routes = topo.routes ?? [];
  const peers = topo.peers ?? [];
  console.log(`AS24940 announces ${routes.length} routes, peers with ${peers.length} ASNs`);
}

Enter fullscreen mode Exit fullscreen mode

Both functions mirror the Python version. AbortSignal.timeout(5000) prevents hung connections from blocking your pipeline. The ?? (nullish coalescing) operator handles missing fields without throwing.

Reading the Response: What Each Field Tells You

The basic ASN response contains eleven fields. Here's what matters for security work:

as_number: The ASN itself, formatted with the "AS" prefix (e.g., "AS15169"). This is the identifier you'll use for blocking rules, SIEM correlation, and threat feed matching.

organization: The entity registered with the RIR as the AS holder. For cloud providers, this is the provider, not the customer. AS16509 is "Amazon.com, Inc." even though the IP you're investigating belongs to a fintech startup running on AWS.

type: One of ISP, HOSTING, BUSINESS, or EDUCATION. This is your first-pass risk signal. HOSTING ASNs produce a disproportionate share of automated attacks. ISP ASNs carry mostly residential traffic. EDUCATION ASNs are universities.

country: ISO 3166-1 alpha-2 country code for the AS registration. Not necessarily where the traffic originates (a Hetzner server in Finland still shows "country": "DE" because Hetzner is registered in Germany).

rir: Which Regional Internet Registry manages the ASN. Useful for abuse reporting: RIPE NCC has different abuse processes than ARIN.

routes (with include=routes): The IP prefixes this AS announces to the internet. This is the AS's footprint. If you see malicious traffic from 5 IPs and all fall within prefixes announced by the same ASN, that confirms you're dealing with one network.

peers, upstreams, downstreams (with include=peers,upstreams,downstreams): The network's BGP relationships. Upstreams are transit providers (who the AS pays for internet access). Downstreams are customer networks. Peers are networks exchanging traffic directly. For investigation, downstreams are particularly interesting: they reveal smaller networks hiding behind a hosting provider's ASN.

Putting It Together: An Investigation Walkthrough

You pull your morning alerts and find 47 failed SSH login attempts across 3 IPs overnight: 185.220.101.34, 185.220.101.42, and 185.220.101.52. Different IPs, same /24 range, but you want to confirm this is one actor.

Step 1: Look up the ASN for any of the three IPs. They all resolve to AS205100, F3 Netze e.V., registered in Germany. The organization name and the related prefix data point to Tor-exit traffic. That reframes the incident immediately: you're not dealing with a compromised residential user, you're dealing with traffic deliberately routed through an anonymization network.

Step 2: Pull the topology with include=routes,upstreams. The routes show a small number of prefixes (the 185.220.101.0/24 range among them). The upstreams show two transit providers. A stub-like topology for what is effectively a single-purpose network.

Step 3: Check your SIEM for historical traffic from AS205100. You find it appears in 12 previous incidents over the past 90 days, all brute-force attempts. That's a pattern.

Step 4: Decision. Hard-blocking AS205100 is reasonable here because it's a single-purpose Tor exit network, and your service has no legitimate users routing through Tor exits. For a multi-purpose hosting ASN like Hetzner or DigitalOcean, you'd rate-limit instead of block.

The whole investigation took 4 API calls and 3 minutes. Without ASN data, you'd have looked up 3 IPs individually, found they're "in Germany," and missed the network-level pattern entirely.

Caching and Performance Notes

ASN assignments change infrequently. An IP's ASN changes when the IP block is sold or reallocated, which happens on the order of months or years, not hours. A 24-hour TTL on cached ASN lookups is aggressive enough for most security use cases without wasting quota on redundant calls.

For high-volume enrichment (processing millions of log entries), consider a local database instead of per-request API calls. MMDB-format ASN databases eliminate network latency entirely and support the same lookup patterns. The trade-off is update frequency: you're working with a daily snapshot instead of real-time data. For most enrichment pipelines, that's acceptable.

When the ASN API is unreachable, fail open. ASN enrichment adds context but isn't load-bearing for most security decisions. Log the unenriched event and backfill later rather than dropping traffic or blocking alerts because the lookup timed out.

A Few Extra Notes

Private ASNs exist. The ranges 64512-65534 (16-bit) and 4200000000-4294967294 (32-bit) are reserved for private use. These are used internally by organizations with complex routing but should never appear in public BGP tables. If you see a private ASN in external traffic, something is misconfigured upstream, and that's worth investigating on its own.

ASN data is one signal, not a verdict. AS16509 (Amazon AWS) hosts both legitimate SaaS products and attack infrastructure. Blocking it would break half the internet. Use ASN data to inform decisions (add friction, increase logging, require CAPTCHA), not to make them unilaterally.

The organization field can mislead. Cloud providers sublease IP ranges. The ASN registrant is Amazon, Google, or Microsoft, but the actual user of the IP is whoever rented that instance. For cloud ASNs, pair ASN data with reverse DNS, TLS certificate inspection, or company-level enrichment to get closer to the real operator.

For a deeper reference on autonomous systems, BGP routing, and ASN assignment history, the ARIN ASN guide and the ipgeolocation.io ASN guide both cover the conceptual side in more depth than this tutorial has room for.