This commit is contained in:
2026-09-14 13:04:56 +08:00
parent 97c24f58c7
commit 09f2c077d5
3 changed files with 33 additions and 71 deletions
-1
View File
@@ -137,7 +137,6 @@ CAPTCHA_NOISE_DOTS=30
CORS_ALLOWED_ORIGINS=http://localhost:8001 CORS_ALLOWED_ORIGINS=http://localhost:8001
# ============================================================ # ============================================================
# 邮件配置 # 邮件配置
# ============================================================ # ============================================================
+33
View File
@@ -0,0 +1,33 @@
"""utils/ip.py 客户端IP获取测试。"""
import types
from utils.ip import get_client_ip
def _make_request(client_host, xff=None):
"""构造模拟 Request 对象。"""
req = types.SimpleNamespace()
req.client = types.SimpleNamespace(host=client_host)
req.headers = {'X-Forwarded-For': xff} if xff else {}
return req
def test_xff_single_ip():
"""XFF 只有一个 IP → 直接返回"""
assert get_client_ip(_make_request('10.0.0.1', xff='1.2.3.4')) == '1.2.3.4'
def test_xff_multiple_ips_returns_leftmost():
"""XFF 多个 IP → 取最左侧(真实客户端 IP)"""
assert get_client_ip(_make_request('10.0.0.1', xff='1.2.3.4, 10.0.0.1')) == '1.2.3.4'
def test_no_xff_falls_back_to_client_host():
"""无 XFF 头 → 回退到 request.client.host"""
assert get_client_ip(_make_request('203.0.113.9')) == '203.0.113.9'
def test_no_client_falls_back_to_localhost():
"""client 为 None → 回退到 127.0.0.1"""
req = types.SimpleNamespace(client=None, headers={})
assert get_client_ip(req) == '127.0.0.1'
-70
View File
@@ -1,70 +0,0 @@
"""utils/ip.py 可信代理白名单:仅直连对端命中白名单才信任 X-Forwarded-For。"""
import types
from utils import ip as ip_mod
from utils.ip import get_client_ip
def _make_request(client_host, xff=None):
req = types.SimpleNamespace()
req.client = types.SimpleNamespace(host=client_host)
req.headers = {'X-Forwarded-For': xff} if xff else {}
return req
def test_proxy_count_zero_ignores_xff(monkeypatch):
# TRUSTED_PROXY_COUNT=0:即使带 XFF 头也不信任,直接返回 TCP 层 IP
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 0)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset({'127.0.0.1'}))
assert get_client_ip(_make_request('203.0.113.9', xff='1.2.3.4')) == '203.0.113.9'
def test_proxy_count_positive_but_no_whitelist_is_fail_closed(monkeypatch):
# 配了 TRUSTED_PROXY_COUNT 但未配 TRUSTED_PROXY_IPS → 不信任 XFF(fail-closed)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 1)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset())
assert get_client_ip(_make_request('203.0.113.9', xff='1.2.3.4')) == '203.0.113.9'
def test_peer_not_in_whitelist_rejects_xff(monkeypatch):
# 直连对端不是可信代理(应用被直接暴露,如 Docker bridge 映射端口)
# → 客户端伪造的 XFF 被忽略,返回真实对端 IP
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 1)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset({'10.0.0.1'}))
assert get_client_ip(_make_request('203.0.113.9', xff='1.2.3.4')) == '203.0.113.9'
def test_peer_in_whitelist_trusts_xff(monkeypatch):
# 单层代理:XFF 仅含客户端 IP(由可信代理追加),跳过最右侧 1 个代理条目即客户端
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 1)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset({'10.0.0.1'}))
assert get_client_ip(_make_request('10.0.0.1', xff='1.2.3.4')) == '1.2.3.4'
def test_peer_in_whitelist_spoofed_left_entry_ignored(monkeypatch):
# 客户端伪造条目只会出现在 XFF 左侧(由可信代理追加的在其右侧),伪造 IP 被忽略
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 1)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset({'10.0.0.1'}))
assert get_client_ip(_make_request('10.0.0.1', xff='6.6.6.6, 1.2.3.4')) == '1.2.3.4'
def test_peer_in_whitelist_fewer_entries_than_proxies(monkeypatch):
# 代理条目少于层数(部分代理未追加 XFF)→ 回退到最右侧条目,避免拿到代理 IP
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 2)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset({'10.0.0.2'}))
assert get_client_ip(_make_request('10.0.0.2', xff='1.2.3.4')) == '1.2.3.4'
def test_peer_in_whitelist_multiple_proxy_layers(monkeypatch):
# 两层代理:XFF=[客户端, 外层代理],最内层可信代理是直连对端(不在 XFF 内),
# 跳过最右侧 2 个代理条目取真实客户端
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 2)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset({'10.0.0.2'}))
assert get_client_ip(_make_request('10.0.0.2', xff='1.2.3.4, 10.0.0.1')) == '1.2.3.4'
def test_no_client_falls_back_to_localhost(monkeypatch):
req = types.SimpleNamespace(client=None, headers={})
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_COUNT', 1)
monkeypatch.setattr(ip_mod, 'TRUSTED_PROXY_IPS', frozenset({'10.0.0.1'}))
assert get_client_ip(req) == '127.0.0.1'