滥用问题
场景
我的 API 平台越来越受欢迎。
两个月后:
- 日调用量:50 万次
- 看起来一切正常 ✅
那时候的我还不明白,危险正在逼近。
异常发现
某天早上,我收到了外部 API 提供商的邮件:
【重要通知】调用配额异常
尊敬的用户:
我们检测到您的 API 调用异常:
- 昨天:00:00-06:00,调用量异常
- 6 小时内调用了:8000 次
- 正常情况:全天约 5000 次
请检查是否有异常调用。
如继续异常,我们将暂停服务。看到邮件的那一刻,我后背发凉:6 小时调用了 8000 次?
这意味着有人在 24 小时内可以消耗我 32000 次配额,而我的免费额度只有 10000 次/天。
调查过程
我赶紧查看日志,开始写分析脚本:
# 分析日志
import logging
from collections import defaultdict
# 统计每个 IP 的调用量
ip_calls = defaultdict(int)
with open('access.log') as f:
for line in f:
ip = extract_ip(line)
ip_calls[ip] += 1
# 找出 Top 调用者
top_ips = sorted(ip_calls.items(), key=lambda x: x[1], reverse=True)[:10]
print("Top 10 IP:")
for ip, count in top_ips:
print(f"{ip}: {count} 次")结果让我震惊:
Top 10 IP:
203.0.113.1: 7500 次 ← 异常!
198.51.100.1: 150 次
198.51.100.2: 120 次
...发现异常 IP:203.0.113.1
深入分析
我查看了这个 IP 的调用日志:
grep "203.0.113.1" access.log | tail -20
输出:
10:00:01 GET /api/weather?city=北京
10:00:01 GET /api/weather?city=上海
10:00:02 GET /api/weather?city=深圳
10:00:02 GET /api/weather?city=广州
10:00:03 GET /api/weather?city=杭州
10:00:03 GET /api/weather?city=南京
...特征:
- 每秒调用 2-3 次
- 轮流查询不同城市
- 持续不断,没有停止
结论:有人在爬取数据!
问题严重性
我计算了一下:
这个爬虫的调用情况:
- 每秒:2-3 次
- 每分钟:约 150 次
- 每小时:约 9000 次
- 每天:约 216000 次
占用的外部 API 配额:
- 我的限额:10000 次/天
- 这个爬虫:216000 次/天
- 影响:会让我的账号被限流!更糟糕的是:
- 正常用户无法访问(外部 API 限流)
- 我的服务不可用
- 用户开始投诉
那一刻,我真的慌了。
尝试解决
方案 1:封禁 IP
我先把异常 IP 封禁了:
BLOCKED_IPS = {'203.0.113.1'}
@app.route('/api/weather')
def get_weather():
client_ip = request.remote_addr
if client_ip in BLOCKED_IPS:
return jsonify({'error': 'IP blocked'}), 403
# ... 正常逻辑效果:
- 这个爬虫被阻止了 ✅
- 但几天后,又出现了新的爬虫 IP ❌
我知道,这是场猫鼠游戏,我追不上。
方案 2:限制单个 IP 的调用频率
from collections import defaultdict
import time
# 记录每个 IP 的调用时间
ip_calls = defaultdict(list)
@app.route('/api/weather')
def get_weather():
client_ip = request.remote_addr
current_time = time.time()
# 检查这个 IP 在最近 1 分钟内调用了多少次
recent_calls = [t for t in ip_calls[client_ip] if current_time - t < 60]
if len(recent_calls) > 60: # 每分钟最多 60 次
return jsonify({'error': 'Rate limit exceeded'}), 429
# 记录这次调用
ip_calls[client_ip].append(current_time)
# ... 正常逻辑效果:
- 限制了单个 IP 的调用频率 ✅
- 但如果有人用很多个 IP 呢?❌
- IP 记录越来越多,内存占用增加 ❌
这个方案还是治标不治本。
方案 3:关键问题
那天晚上我在想:为什么任何人都能随意调用我的 API?
问题在于:没有门槛。
我需要一种机制来:
- 识别谁在调用
- 限制每个用户的调用配额
- 防止滥用
解决思路
我需要引入用户认证系统。
核心概念:
每个用户注册 → 获得唯一的 API Key
每次调用 → 必须携带 API Key
根据 API Key → 识别用户身份、限制配额┌─────────────┐
│ 开发者 │
└──────┬──────┘
│
│ 1. 注册
▼
┌─────────────────────┐
│ 我的平台 │
│ - 分配 API Key │
│ - 设置调用配额 │
└──────┬──────────────┘
│
│ 2. 调用 API (携带 API Key)
▼
┌─────────────────────┐
│ 验证 API Key │
│ - 检查是否有效 │
│ - 检查配额 │
└──────┬──────────────┘
│
│ 3. 返回数据
▼
┌─────────────┐
│ 成功/失败 │
└─────────────┘这是最彻底,但也最复杂的方案。
作为 solo founder,我的时间有限。但我明白,有些路再难走也要走。
我决定:干。
下一步
我决定实现用户认证系统。
具体包括:
- 用户注册功能
- API Key 生成和管理
- 调用时验证 API Key
- 每个用户的配额限制
这将是一场硬仗。
