ip-security-analyzer-cloudflare-worker

v2026.09.24

Deploy and customize a Cloudflare Worker for IP intelligence, VPN/proxy detection, WebRTC leak testing, and security scoring with IPinfo and AbuseIPDB integration

GitHub
安装命令
npx skhub add reason-machines/ip-security-analyzer-cloudflare-worker
Markdown
SKILL.md

IP Security Analyzer Cloudflare Worker

Skill by ara.so — Security Skills collection

Overview

IP Security Analyzer is a single-file Cloudflare Worker that provides forensic IP intelligence by combining Cloudflare metadata, IPinfo Lite ASN enrichment, AbuseIPDB reputation data, request header consistency checks, and browser-side WebRTC leak detection. It separates network classification from risk scoring — infrastructure IPs (datacenter, CDN, cloud) are identified but not penalized unless abuse indicators exist.

Key capabilities:

  • Public IP detection and network identity analysis
  • ASN and ISP classification (hosting, mobile, residential, VPN/proxy)
  • Abuse reputation scoring via AbuseIPDB
  • Request header consistency checks (automation detection)
  • Browser WebRTC candidate leak testing
  • Transparent risk scoring model (starts at 100, penalized only by evidence)
  • RESTful JSON API and modern HTML dashboard

Installation

Option 1: Cloudflare Dashboard

  1. Open Cloudflare Dashboard → Workers & Pages
  2. Click Create Worker
  3. Name it (e.g., ip-security-analyzer)
  4. Click Edit Code
  5. Paste the Worker code from the repository
  6. Click Save and Deploy

Option 2: Wrangler CLI

# Initialize project
npx wrangler init ip-security-analyzer
cd ip-security-analyzer

# Create worker file
cat > src/index.js << 'EOF'
// Paste the full Worker code here
EOF

# Deploy
npx wrangler deploy

Setting Up External APIs

The Worker functions without external keys but enrichment is recommended:

# Add secrets via Wrangler
npx wrangler secret put IPINFO_TOKEN
# Enter your IPinfo Lite token

npx wrangler secret put ABUSEIPDB_KEY
# Enter your AbuseIPDB API key

Get API keys:

Alternatively, add via Cloudflare Dashboard:

Worker → Settings → Variables and Secrets → Add Variable

Core API Endpoints

GET /json or /api

Returns full server-side IP analysis as JSON.

// Example fetch from client
const response = await fetch('https://your-worker.workers.dev/json');
const data = await response.json();

console.log(data.ip.address);          // "203.0.113.42"
console.log(data.network.asn);         // 15169
console.log(data.network.isp);         // "Google LLC"
console.log(data.risk.score);          // 100
console.log(data.risk.verdict);        // "Low Risk"
console.log(data.risk.tags);           // ["Hosting/Datacenter"]

Response structure:

{
  "status": "success",
  "ip": {
    "address": "203.0.113.42",
    "version": "IPv4"
  },
  "network": {
    "asn": 15169,
    "isp": "Google LLC",
    "localClassification": {
      "type": "Hosting / Datacenter",
      "flags": {
        "hostingName": true,
        "vpnProxyName": false,
        "abuseDatacenterUsage": true
      }
    }
  },
  "location": {
    "country": "US",
    "region": "California",
    "city": "Mountain View"
  },
  "externalIntel": {
    "ipinfoLite": {
      "enabled": true,
      "ok": true,
      "asn": "AS15169",
      "org": "Google LLC"
    },
    "abuseipdb": {
      "enabled": true,
      "ok": true,
      "abuseConfidenceScore": 0,
      "totalReports": 0,
      "isTor": false,
      "usageType": "Data Center/Web Hosting/Transit"
    }
  },
  "cloudflare": {
    "colo": "SJC",
    "tlsVersion": "TLSv1.3",
    "httpProtocol": "HTTP/2",
    "ray": "abc123..."
  },
  "risk": {
    "score": 100,
    "verdict": "Low Risk",
    "tags": ["Hosting/Datacenter"],
    "findings": []
  }
}

POST /report

Receives browser WebRTC candidates and returns combined risk analysis.

// Client-side WebRTC leak test
async function testWebRTC() {
  const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
  const candidates = [];

  pc.onicecandidate = (event) => {
    if (event.candidate) {
      candidates.push({
        candidate: event.candidate.candidate,
        type: event.candidate.type,
        protocol: event.candidate.protocol
      });
    } else {
      // All candidates gathered
      sendCandidates(candidates);
    }
  };

  pc.createDataChannel('test');
  const offer = await pc.createOffer();
  await pc.setLocalDescription(offer);
}

async function sendCandidates(candidates) {
  const response = await fetch('/report', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ candidates })
  });
  
  const result = await response.json();
  console.log('Risk score:', result.risk.score);
  console.log('WebRTC findings:', result.webrtc);
}

Response includes:

{
  "webrtc": {
    "publicIPs": ["198.51.100.42"],
    "privateIPs": ["192.168.1.100"],
    "mdnsIPs": ["abcd1234-5678-90ab-cdef-1234567890ab.local"],
    "mismatch": true,
    "httpIP": "203.0.113.42"
  },
  "risk": {
    "score": 75,
    "findings": [
      {
        "category": "WebRTC Leak",
        "severity": "High",
        "message": "WebRTC exposed different public IP: 198.51.100.42 vs HTTP IP 203.0.113.42",
        "points": -15
      }
    ]
  }
}

GET /health

Basic status endpoint.

curl https://your-worker.workers.dev/health
{
  "status": "healthy",
  "version": "1.0.0",
  "timestamp": "2026-07-29T13:48:27Z"
}

Configuration

Manual API Key Fallback

If you cannot use Cloudflare secrets, edit the Worker code:

// At the top of the Worker file
const MANUAL_IPINFO_TOKEN = ""; // Add your IPinfo token here
const MANUAL_ABUSEIPDB_KEY = ""; // Add your AbuseIPDB key here

// The Worker checks env.IPINFO_TOKEN first, then falls back to MANUAL_IPINFO_TOKEN

⚠️ Important: Never commit API keys to public repositories. Use environment variables in production.

Custom Domain Setup

Attach a custom domain via Cloudflare Dashboard:

Workers & Pages → Your Worker → Settings → Triggers → Add Custom Domain

Example: myip.example.com

Customizing Risk Scoring

The risk model is defined in the Worker code. Default penalties:

// Example from the Worker scoring logic
const scoringModel = {
  tor: -50,                           // Tor exit node
  abuseConfidence: -1,                // Per point of AbuseIPDB confidence
  abuseReports: {
    low: -5,      // 1-10 reports
    medium: -15,  // 11-50 reports
    high: -30     // 50+ reports
  },
  vpnProxyName: -20,                  // VPN/proxy keyword in ISP name
  automationUA: -15,                  // curl, wget, python, selenium
  noUserAgent: -10,
  missingHeaders: -5,
  oldTLS: -10,                        // Non-TLS 1.3
  botScoreLow: -20,                   // Cloudflare Bot Management < 30
  webrtcMismatch: -15,                // Different public IP via WebRTC
  webrtcPrivate: -5                   // Private IP exposure
};

To customize, locate the risk calculation functions in the Worker code and adjust penalty values.

Network Classification

The Worker classifies networks using heuristic ASN/ISP name matching:

// Classification keywords (conceptual, from Worker code)
const classificationPatterns = {
  hosting: ['cloud', 'hosting', 'datacenter', 'server', 'vps', 'colocation', 'transit'],
  vpnProxy: ['vpn', 'proxy', 'tunnel', 'privacy', 'tor', 'anonymizer'],
  mobile: ['mobile', 'wireless', 'cellular', 'lte', '5g', '4g'],
  education: ['university', 'college', 'edu', 'academic'],
  corporate: ['corporate', 'enterprise', 'business']
};

Classification types:

  • Hosting / Datacenter
  • Mobile / Cellular
  • Corporate / Business
  • Education / Campus
  • VPN / Proxy / Privacy
  • Likely Residential
  • Unknown

Important: Hosting/datacenter classification does NOT reduce risk score by itself.

Common Patterns

Embedding IP Analysis in Your Application

// Server-side fetch example
export default {
  async fetch(request, env) {
    // Get client IP
    const clientIP = request.headers.get('CF-Connecting-IP');
    
    // Call the IP Security Worker
    const analysis = await fetch(`https://your-worker.workers.dev/json?ip=${clientIP}`);
    const data = await analysis.json();
    
    // Make decisions based on risk
    if (data.risk.score < 50) {
      return new Response('Access denied - high risk IP', { status: 403 });
    }
    
    if (data.network.localClassification.flags.vpnProxyName) {
      console.log('VPN detected:', data.network.isp);
    }
    
    return new Response('Access granted');
  }
};

Custom WebRTC Leak Detector

class WebRTCLeakDetector {
  constructor(reportEndpoint) {
    this.endpoint = reportEndpoint;
    this.candidates = [];
  }

  async test() {
    const config = {
      iceServers: [
        { urls: 'stun:stun.l.google.com:19302' },
        { urls: 'stun:stun1.l.google.com:19302' }
      ]
    };
    
    const pc = new RTCPeerConnection(config);
    
    return new Promise((resolve, reject) => {
      pc.onicecandidate = (event) => {
        if (event.candidate) {
          this.candidates.push({
            candidate: event.candidate.candidate,
            type: event.candidate.type,
            protocol: event.candidate.protocol,
            address: event.candidate.address
          });
        } else {
          // Gathering complete
          this.report().then(resolve).catch(reject);
        }
      };

      pc.onicegatheringstatechange = () => {
        if (pc.iceGatheringState === 'complete') {
          setTimeout(() => this.report().then(resolve), 1000);
        }
      };

      pc.createDataChannel('leak-test');
      pc.createOffer()
        .then(offer => pc.setLocalDescription(offer))
        .catch(reject);
    });
  }

  async report() {
    const response = await fetch(this.endpoint, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ candidates: this.candidates })
    });
    
    return await response.json();
  }

  extractIPs() {
    const ips = new Set();
    this.candidates.forEach(c => {
      const match = c.candidate.match(/(\d+\.\d+\.\d+\.\d+)/);
      if (match) ips.add(match[1]);
    });
    return Array.from(ips);
  }
}

// Usage
const detector = new WebRTCLeakDetector('/report');
const result = await detector.test();
console.log('Public IPs:', result.webrtc.publicIPs);
console.log('Private IPs:', result.webrtc.privateIPs);
console.log('Mismatch detected:', result.webrtc.mismatch);

Checking Specific IP Address

// Query analysis for a specific IP
async function analyzeIP(targetIP) {
  const response = await fetch(`https://your-worker.workers.dev/json?ip=${targetIP}`);
  const data = await response.json();
  
  return {
    isVPN: data.network.localClassification.flags.vpnProxyName,
    isTor: data.externalIntel.abuseipdb.isTor,
    isHosting: data.network.localClassification.type.includes('Hosting'),
    riskScore: data.risk.score,
    abuseScore: data.externalIntel.abuseipdb.abuseConfidenceScore,
    country: data.location.country,
    asn: data.network.asn,
    isp: data.network.isp
  };
}

const result = await analyzeIP('1.1.1.1');
console.log(result);
// {
//   isVPN: false,
//   isTor: false,
//   isHosting: true,
//   riskScore: 100,
//   abuseScore: 0,
//   country: 'AU',
//   asn: 13335,
//   isp: 'Cloudflare, Inc.'
// }

Troubleshooting

External API Failures

If IPinfo or AbuseIPDB fails, the Worker continues with Cloudflare metadata only:

{
  "externalIntel": {
    "ipinfoLite": {
      "enabled": true,
      "ok": false,
      "error": "API request failed"
    },
    "abuseipdb": {
      "enabled": false,
      "reason": "No API key configured"
    }
  }
}

Fix:

  • Verify API keys are correctly set in Cloudflare secrets
  • Check API key validity and quota limits
  • Review Worker logs: npx wrangler tail

WebRTC Test Returns Empty Candidates

Causes:

  • Browser privacy settings block WebRTC
  • VPN/browser extension blocks ICE gathering
  • STUN server unreachable

Solution:

// Add timeout and error handling
const testWithTimeout = (timeout = 5000) => {
  return Promise.race([
    detector.test(),
    new Promise((_, reject) => 
      setTimeout(() => reject(new Error('WebRTC timeout')), timeout)
    )
  ]).catch(error => {
    console.log('WebRTC blocked or failed:', error.message);
    return { blocked: true };
  });
};

Score Always 100 Despite VPN

The Worker does not penalize infrastructure IPs by default. Score decreases only with evidence:

Check for:

  • VPN/proxy keywords in ISP name (data.network.localClassification.flags.vpnProxyName)
  • Abuse reports (data.externalIntel.abuseipdb.totalReports)
  • WebRTC mismatch (data.webrtc.mismatch)
  • Automation headers (data.risk.findings)

Example high-risk VPN:

{
  "network": {
    "isp": "NordVPN",
    "localClassification": {
      "type": "VPN / Proxy / Privacy",
      "flags": { "vpnProxyName": true }
    }
  },
  "risk": {
    "score": 80,
    "findings": [
      { "category": "Network Classification", "message": "VPN/Proxy keyword detected", "points": -20 }
    ]
  }
}

Custom Domain Shows Worker URL

After adding custom domain:

  1. Wait 1-2 minutes for DNS propagation
  2. Verify DNS record in Cloudflare DNS settings
  3. Check Worker triggers: Settings → Triggers → Routes

CORS Issues When Calling from Browser

Add CORS headers in Worker response:

// In the Worker code, modify response headers
const headers = {
  'Content-Type': 'application/json',
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type'
};

// Handle preflight
if (request.method === 'OPTIONS') {
  return new Response(null, { headers });
}

Advanced Usage

Integrating with Cloudflare Bot Management

If you have Cloudflare Bot Management enabled:

// Worker automatically reads bot score from request.cf
const botScore = request.cf?.botManagement?.score || null;

// Low bot score reduces risk score
if (botScore !== null && botScore < 30) {
  findings.push({
    category: 'Bot Detection',
    severity: 'High',
    message: `Low bot score detected: ${botScore}`,
    points: -20
  });
}

Rate Limiting Analysis Endpoint

// Add KV namespace for rate limiting
export default {
  async fetch(request, env) {
    const ip = request.headers.get('CF-Connecting-IP');
    const key = `ratelimit:${ip}`;
    
    // Check rate limit (5 requests per minute)
    const count = await env.RATE_LIMIT.get(key);
    if (count && parseInt(count) > 5) {
      return new Response('Rate limit exceeded', { status: 429 });
    }
    
    // Increment
    const newCount = count ? parseInt(count) + 1 : 1;
    await env.RATE_LIMIT.put(key, newCount.toString(), { expirationTtl: 60 });
    
    // Continue with analysis...
  }
};

Logging and Monitoring

// Add structured logging
const logAnalysis = (ip, result) => {
  console.log(JSON.stringify({
    timestamp: new Date().toISOString(),
    ip: ip,
    asn: result.network.asn,
    isp: result.network.isp,
    riskScore: result.risk.score,
    verdict: result.risk.verdict,
    findings: result.risk.findings.length
  }));
};

// View logs
// npx wrangler tail --format pretty

This skill provides comprehensive guidance for deploying, configuring, and integrating the IP Security Analyzer Cloudflare Worker for network forensics, VPN/proxy detection, and IP intelligence analysis.

发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

2026年9月24日

分类

未分类

许可证

NOASSERTION

源路径

skills/ip-security-analyzer-cloudflare-worker

默认分支

main

最新提交

304c245

Tree SHA

010f47d