导航菜单

滥用问题

场景

我的 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,我的时间有限。但我明白,有些路再难走也要走。

我决定:干。

下一步

我决定实现用户认证系统。

具体包括:

  1. 用户注册功能
  2. API Key 生成和管理
  3. 调用时验证 API Key
  4. 每个用户的配额限制

这将是一场硬仗。

搜索