튜토리얼: Node.js Express에서 VPN 및 Tor 사용자를 감지하는 방법
Source: Dev.to
개요
공개 API, SaaS, 포럼 등 어떤 서비스를 운영하든 이미 겪고 있는 고통: 봇 트래픽.
스팸을 이유로 사용자를 차단하면, 몇 초 뒤에 VPN을 전환해서 새 계정으로 다시 돌아옵니다. IP를 차단하면, 그들은 Tor exit 노드로 전환합니다.
이 튜토리얼에서는 Node.js 애플리케이션에서 비거주 IP(VPN, 프록시, 호스팅 센터)를 감지하는 방법을 보여드리겠습니다. 이를 통해 데이터베이스에 접근하기 전에 차단하거나 최소한 CAPTCHA로 도전하도록 할 수 있습니다.
목표
우리는 Express에서 다음과 같은 미들웨어 함수를 원합니다:
app.use((req, res, next) => {
if (isHighRisk(req.ip)) {
return res.status(403).send("VPNs are not allowed.");
}
next();
});
다음은 이를 구축하는 방법입니다.
Method 1: The “Hard” Way (Self‑Hosted Lists)
Step 1: Get the Data
- Tor Exit Nodes – Tor 프로젝트는 종료 주소 목록을 공개합니다.
- Cloud Ranges – AWS와 Google은 대용량 JSON 파일 형태로 IP 범위를 공개합니다.
이 파일들(예: tor-exit-nodes.txt, aws-ip-ranges.json)을 다운로드하고 최신 상태로 유지해야 합니다.
Step 2: The Code
const fs = require('fs');
const ipRangeCheck = require('ip-range-check'); // npm install ip-range-check
// Load the massive lists into memory (careful with RAM!)
const torNodes = fs.readFileSync('tor-exit-nodes.txt', 'utf8')
.split('\n')
.filter(Boolean);
const awsRanges = JSON.parse(
fs.readFileSync('aws-ip-ranges.json', 'utf8')
).prefixes.map(p => p.ip_prefix);
function isHighRisk(userIp) {
// Check if IP is in the Tor list
if (torNodes.includes(userIp)) return true;
// Check if IP is in a Cloud Range (CPU intensive)
if (ipRangeCheck(userIp, awsRanges)) return true;
return false;
}
The Problem with Method 1
- Stale data – VPN 제공업체는 IP를 매일 교체합니다. 시간당 업데이트가 없으면 많은 공격을 놓치게 됩니다.
- Memory hog – 수백만 개의 IP를 Node.js 메모리에 로드하면 서버가 크래시될 수 있습니다.
- False positives – 정상적인 데이터센터 IP와 악의적인 VPN IP를 구분하는 것이 어려울 수 있습니다.
Method 2: The “Easy” Way (Live API Lookup)
Step 1: Get a Free API Key
Grab a free key from the provider’s website (no credit card required).
Step 2: The Middleware
The API returns a trustScore (0‑100) along with flags for Tor and VPN.
const axios = require('axios');
async function checkRiskScore(req, res, next) {
const userIp = req.ip;
try {
const response = await axios.get('https://candycorndb.com/api/public/ip-score', {
params: { ip: userIp }
});
const { score, isTor, isVPN } = response.data;
// BLOCK if it's a confirmed Tor node or very high risk
if (isTor || score > 85) {
return res.status(403).json({ error: 'Anonymizers not allowed.' });
}
// CHALLENGE if it's suspicious (e.g., DigitalOcean droplet)
if (score >= 50) {
// Insert CAPTCHA logic here…
console.log(`Suspicious traffic from ${userIp}`);
}
next();
} catch (err) {
// Fail open: if the API is down, let the user in so you don’t block real people
next();
}
}
// Apply to your sensitive routes
app.post('/api/signup', checkRiskScore, (req, res) => {
res.send("Account created!");
});
Why This Is Better
- Just‑in‑time scanning – If the API hasn’t seen the IP before, it scans open ports and ISP data in < 500 ms, so you never get “unknown.”
- No maintenance – No need to download daily CSV dumps.
- Saves RAM – Your Node server handles logic, not massive IP storage.
Summary
악성 IP 차단은 일종의 무기 경쟁입니다. 작은 취미 프로젝트를 만들고 있다면 방법 1은 재미있는 학습 연습이 될 수 있습니다. 프로덕션 앱의 경우, 위험 감지를 전용 API(방법 2)로 넘기는 것이 스팸 계정을 해제하는 데 드는 시간보다 보통 더 저렴합니다.
IP 필터링 로직에 대해 언제든지 질문해 주세요! 😅