Python HTTP请求安全:中间人攻击原理与防御实战指南

📅 2026/6/17 22:49:37
Python HTTP请求安全:中间人攻击原理与防御实战指南
1. 项目概述为什么Python开发者必须警惕HTTP请求劫持如果你用Python写过爬虫、调用过API、或者开发过任何需要网络通信的应用那你一定对requests.get()或httpx.post()这样的代码再熟悉不过了。在本地开发环境你可能随手就写了个http://localhost:5000在测试时为了图方便也可能直接访问了没有HTTPS的内部服务。但你想过没有这些看似平常的HTTP请求在传输过程中可能正被一双“眼睛”盯着甚至被一双“手”随意篡改。这就是我们今天要深入探讨的“中间人攻击”。我见过太多项目在开发阶段对安全满不在乎觉得“内部网络很安全”或者“测试数据不重要”。结果一旦部署到稍微复杂一点的网络环境比如公共Wi-Fi、共享办公网络或者遭遇内网渗透轻则敏感数据泄露重则业务逻辑被恶意篡改造成难以挽回的损失。中间人攻击不是电影里的黑客专属它门槛不高利用的就是我们日常开发中那些不经意的安全疏忽。这篇文章我将从一个实践者的角度拆解中间人攻击在Python HTTP通信场景下的原理、复现手法并给出从代码层面到架构层面的、可落地的防御技巧。这不是一篇照本宣科的理论文章而是我踩过坑、交过学费后总结出的实战指南。2. 中间人攻击原理深度拆解不只是“窃听”那么简单很多人对中间人攻击的理解还停留在“窃听”层面认为攻击者只是被动地复制一份数据。这种想法太天真了。在现代攻击中中间人更是一个“活跃的”参与者它拦截、审查、并可能篡改通信双方的所有数据。2.1 攻击的底层逻辑如何成为那个“中间人”要让通信的双方客户端和服务器都毫无察觉地把数据发给你攻击者需要先“插入”到通信链路中。常见的手法有几种ARP欺骗这在内网中尤其有效。我们的电脑通过IP地址通信但实际的数据包是通过MAC地址在局域网内传输的。ARP协议负责把IP地址翻译成MAC地址。攻击者可以持续地向网络广播虚假的ARP响应包宣称“目标服务器的IP地址对应的MAC地址是我这个攻击机器的MAC”。于是原本要发给服务器的数据包全都被网关错误地转发到了攻击者的机器上。攻击者拿到数据后可以原封不动地转发给真正的服务器从而神不知鬼不觉地成为中间人。用Python的scapy库几行代码就能构造出ARP欺骗包这也是为什么内网安全审计时ARP欺骗检测是必做项。DNS劫持/投毒当你的程序尝试访问api.example.com时会先向DNS服务器查询这个域名对应的IP地址。如果攻击者能够篡改DNS响应比如攻陷了路由器、本地DNS服务器或者利用DNS协议本身的漏洞将api.example.com解析到攻击者控制的IP地址那么你的所有请求自然就送到了攻击者那里。我曾在一些公共咖啡厅的Wi-Fi里亲眼见过这种劫持将某些网站的访问引导到带广告的镜像站。恶意代理与网关这是最直接的方式。在一些企业或公共网络网络管理员可能强制要求所有流量经过一个透明代理或网关进行内容审计或过滤。如果这个节点被攻陷或者其本身就被恶意设置它就是一个天然的中间人。你的Python程序如果使用了系统的代理设置比如通过HTTP_PROXY环境变量那么请求会毫无戒备地流向这个恶意节点。2.2 针对HTTP/HTTPS的不同攻击手法攻击手法会根据目标是明文HTTP还是加密HTTPS而有所不同理解这点对防御至关重要。针对明文HTTP这是最简单的场景因为数据没有任何加密。攻击者拦截到请求和响应后可以窃取信息直接读取请求中的Cookie、Authorization头、POST表单数据用户名、密码。篡改内容修改服务器返回的HTML页面注入恶意脚本如挖矿代码、键盘记录器篡改API响应数据比如将转账成功的false改为true或者修改商品价格。重定向将响应状态码改为302Location头指向一个钓鱼网站。针对HTTPSHTTPS的设计就是为了防止上述情况它通过SSL/TLS协议对通信进行加密和身份认证。攻击者因此需要更高级的技巧SSL剥离这是最阴险的一招。攻击者拦截客户端发起的HTTPS请求并冒充服务器与客户端进行明文HTTP通信。同时攻击者自己与真正的服务器建立正常的HTTPS连接。这样客户端以为自己在用HTTP可能还会看到浏览器“不安全”的提示但程序不会而它与服务器之间“安全”通道的终端实际上是攻击者。很多安全意识不强的Python脚本如果未强制校验HTTPS就会轻易掉入这个陷阱。伪造证书攻击攻击者自行生成一个SSL证书并利用各种方法让客户端信任这个证书比如事先将伪造的根证书安装到客户端系统的信任库中。这样攻击者就可以用这个“合法”的证书与客户端建立HTTPS连接客户端完全无法察觉。在企业内部有时为了方便监控会采用这种方式部署自签名证书但这本身也引入了风险。降级攻击攻击者干扰客户端和服务器的TLS握手过程迫使双方使用一个存在已知漏洞的、低强度的加密套件如SSLv2, TLS 1.0从而为后续的解密或攻击创造条件。理解这些原理后你就会明白防御的核心不在于“绝对防止被插入”而在于“即使被插入也能及时发现并阻止数据泄露或篡改”。3. 核心防御技术从代码层面筑牢防线知道了攻击怎么来我们就要在Python代码里筑起围墙。很多防御措施并不复杂只是需要养成习惯。3.1 强制使用HTTPS杜绝明文传输这是最基本、最有效的一条。任何时候只要涉及敏感信息就必须使用HTTPS。不要依赖重定向有些服务器配置了HTTP到HTTPS的301/302重定向。你的Python代码可能先发了一个HTTP请求然后跟随重定向到HTTPS。问题在于第一次的HTTP请求就已经暴露在风险中了。攻击者可以在重定向发生前就拦截并响应。正确的做法是在代码中直接使用HTTPS URL并配置客户端拒绝降级。以最常用的requests库为例import requests # 错误示范使用HTTP寄希望于服务器重定向 response requests.get(http://api.example.com/data) # 首次请求即明文 # 正确示范直接使用HTTPS response requests.get(https://api.example.com/data) # 更佳实践使用会话并配置安全选项 session requests.Session() # 可以设置默认请求头要求升级不安全请求虽然主要对浏览器有用但表明了态度 session.headers.update({Upgrade-Insecure-Requests: 1}) # 对于已知只应使用HTTPS的域名可以编写一个适配器来强制检查 from requests.adapters import HTTPAdapter from urllib3.util.url import Url class ForceHTTPSAdapter(HTTPAdapter): def send(self, request, **kwargs): if request.url.startswith(http://): # 在实际项目中这里应该记录警告或直接抛出异常 print(f警告尝试使用不安全的HTTP协议访问 {request.url}) # 可以尝试自动替换为HTTPS但最好是在设计时就确定 # request.url request.url.replace(http://, https://, 1) return super().send(request, **kwargs) session.mount(http://, ForceHTTPSAdapter()) session.mount(https://, HTTPAdapter())注意Upgrade-Insecure-Requests是一个HTTP请求头主要被浏览器识别用于告诉服务器“我支持HTTPS请给我HTTPS内容”。对于Python的requests库服务器不一定理会这个头。因此最根本的还是直接使用HTTPS URL。3.2 强化证书验证确保你连接的是对的人仅仅使用HTTPS还不够还必须验证对方提供的SSL证书是否可信、是否属于它声称的那个域名。requests库默认是开启证书验证的verifyTrue但很多人在开发时为了绕过自签名证书的错误会错误地将其关闭并且忘记在生产环境改回来。# 危险操作完全禁用证书验证相当于“裸奔” response requests.get(https://api.example.com, verifyFalse) # 这会收到一个令人不安的警告InsecureRequestWarning # 正确操作使用默认验证依赖系统信任的CA证书库 response requests.get(https://api.example.com) # 默认 verifyTrue # 如果需要使用特定的CA证书包 response requests.get(https://api.example.com, verify/path/to/certfile.pem) # 或者验证特定的证书文件常用于内部服务 response requests.get(https://internal.example.com, verify/path/to/server-cert.pem)证书验证包含几个关键点有效性证书是否在有效期内。可信性签发证书的机构CA是否在客户端的受信任根证书列表中。域名匹配证书中的Common Name (CN)或Subject Alternative Name (SAN)是否包含了请求的域名。requests底层是urllib3默认会完成所有这些检查。禁用verify就等于告诉程序“不管对方是谁我都信”。这在生产环境是绝对不允许的。3.3 证书钉扎终极身份校验对于安全性要求极高的场景如金融、支付接口仅靠CA验证可能还不够。攻击者可能通过入侵CA或利用某些CA宽松的签发策略获取一个针对你域名的“合法”证书。这时就需要证书钉扎。证书钉扎的原理是客户端预先保存一份它所信任的服务器证书或证书的公钥指纹。当建立连接时不仅检查证书链是否可信还要比对服务器出示的证书是否与本地保存的“钉子”匹配。不匹配则立即终止连接。在Python中实现证书钉扎需要一些额外的工作import requests from requests.adapters import HTTPAdapter from urllib3.util.ssl_ import create_urllib3_context import ssl class PinnedHTTPSAdapter(HTTPAdapter): 一个实现证书钉扎的适配器 def __init__(self, fingerprint, algorithmsha256, **kwargs): :param fingerprint: 预期的证书指纹十六进制字符串如 A1:B2:C3:... :param algorithm: 哈希算法如 sha256, sha1 self.fingerprint fingerprint.upper().replace(:, ) self.algorithm algorithm super().__init__(**kwargs) def init_poolmanager(self, *args, **kwargs): # 创建一个自定义的SSL上下文 context create_urllib3_context() # 我们将在证书验证回调中执行钉扎检查 context.verify_mode ssl.CERT_REQUIRED # 保存指纹到上下文供回调函数使用 context._fingerprint self.fingerprint context._algorithm self.algorithm # 重写验证后的回调函数 def verify_callback(conn, cert, err): # 首先执行默认的证书链验证 if err is not None: return False if cert is None: return False # 计算实际证书的指纹 import hashlib if self.algorithm sha256: digest hashlib.sha256(cert).hexdigest().upper() elif self.algorithm sha1: digest hashlib.sha1(cert).hexdigest().upper() else: raise ValueError(f不支持的算法: {self.algorithm}) # 与预期的指纹比较 if digest ! self.fingerprint: print(f证书指纹不匹配预期: {self.fingerprint}, 实际: {digest}) return False return True context.verify_callback verify_callback kwargs[ssl_context] context return super().init_poolmanager(*args, **kwargs) # 使用方法 # 1. 首先获取你信任的服务器的证书指纹。 # 例如使用openssl命令openssl s_client -connect example.com:443 -servername example.com | openssl x509 -noout -fingerprint -sha256 # 假设获取到的指纹是SHA256 FingerprintA1:B2:C3:... expected_fingerprint A1B2C3... # 去掉冒号 session requests.Session() adapter PinnedHTTPSAdapter(fingerprintexpected_fingerprint) session.mount(https://api.critical-service.com, adapter) try: response session.get(https://api.critical-service.com/secret-data) print(response.json()) except requests.exceptions.SSLError as e: print(fSSL证书验证失败可能是钉扎检查未通过: {e})实操心得证书钉扎虽然安全但缺乏灵活性。如果服务器证书正常轮换到期更新你需要同步更新所有客户端中保存的指纹。因此它通常用于非常固定的、高价值的通信端点或者作为移动App的强化安全手段。在服务端证书可能频繁变化的场景如使用Let‘s Encrypt等短期证书需要设计更复杂的钉扎策略比如钉扎中间CA证书而非叶子证书。3.4 利用安全头部增加攻击难度虽然HTTP安全头部主要靠服务器设置但客户端也可以利用它们来增强防护。Strict-Transport-Security当客户端首次通过HTTPS访问一个网站并收到Strict-Transport-Security头后浏览器会在后续一段时间内由max-age指定自动将所有对该域名的HTTP请求转换为HTTPS。对于Python程序我们可以模拟这种行为在本地缓存这个策略。Content-Security-Policy虽然主要防御XSS但一个严格的CSP可以阻止攻击者注入的恶意脚本在客户端执行间接降低了中间人篡改响应内容的危害。作为客户端我们能做的是检查服务器是否返回了这些安全头部如果没有则视为一个风险点并记录日志。import requests def check_security_headers(url): try: resp requests.get(url, timeout5) security_headers { Strict-Transport-Security: HSTS头缺失。建议服务器配置强制客户端使用HTTPS。, Content-Security-Policy: CSP头缺失。建议配置以减少XSS等风险。, X-Frame-Options: 点击劫持防护头缺失。, X-Content-Type-Options: MIME嗅探防护头缺失。, Referrer-Policy: Referrer策略未设置可能导致信息泄露。 } missing [] for header, description in security_headers.items(): if header not in resp.headers: missing.append(f{header}: {description}) if missing: print(f安全头部检查警告 ({url}):) for msg in missing: print(f - {msg}) else: print(f安全头部检查通过 ({url})) except requests.exceptions.RequestException as e: print(f检查失败 ({url}): {e}) # 检查你的API服务 check_security_headers(https://api.yourdomain.com)4. 高级防御策略与架构思考当你的应用从简单的脚本成长为分布式系统时防御中间人攻击就需要从架构层面进行考量。4.1 双向TLS认证不仅我认你你也得认我在标准的HTTPS中只有客户端验证服务器证书。在双向TLSmTLS中服务器也要求客户端出示证书并进行验证。这就像不仅你要看对方的身份证服务器证书对方也要看你的身份证客户端证书。这通常用于内部微服务之间的通信或者对调用方身份有严格要求的API。服务端配置以Flask Gunicorn为例需配置SSL上下文# 服务端代码片段 (使用Flask) from flask import Flask import ssl app Flask(__name__) app.route(/) def secure_endpoint(): return This is a mutually authenticated endpoint. # 在启动命令中配置Gunicorn非代码内 # gunicorn --bind 0.0.0.0:8443 \ # --keyfile server.key \ # --certfile server.crt \ # --ca-certs client_ca.crt \ # 信任的CA证书用于验证客户端证书 # --ssl-version TLSv1_2 \ # --cert-reqs 2 \ # 2 表示要求并验证客户端证书 # app:app客户端代码使用requestsimport requests # 客户端需要持有自己的证书和私钥以及信任的CA证书用于验证服务器 client_cert (/path/to/client.crt, /path/to/client.key) ca_bundle /path/to/server_ca.crt # 签发服务器证书的CA session requests.Session() # 发送请求时同时提供客户端证书和用于验证服务器证书的CA包 response session.get(https://internal-service:8443/, certclient_cert, verifyca_bundle) print(response.text)注意事项mTLS带来了极高的安全性但管理成本也陡增。你需要一个PKI公钥基础设施来管理服务器和客户端证书的签发、分发、轮换和吊销。对于大量客户端证书管理会成为运维挑战。可以考虑使用服务网格如Istio来透明地注入和管理mTLS。4.2 请求签名与防重放保护数据完整性即使通道是加密的攻击者虽然不能解密内容但有可能将拦截到的有效请求重放多次。例如一个“支付100元”的请求被重放10次就会支付1000元。防御重放攻击和确保请求完整性通常通过请求签名来实现。核心思想客户端在发送请求前用只有自己和服务器知道的密钥对请求的某些关键要素如方法、路径、时间戳、随机数、请求体生成一个签名HMAC并将签名放在请求头如X-Api-Signature中。服务器收到后用同样的密钥和规则重新计算签名如果匹配则证明请求未被篡改且不是旧请求的重放。import hashlib import hmac import time import requests import json from uuid import uuid4 class SignedRequestClient: def __init__(self, api_key, secret_key, base_url): self.api_key api_key self.secret_key secret_key.encode() self.base_url base_url self.session requests.Session() def _generate_signature(self, method, path, timestamp, nonce, bodyNone): 生成请求签名 # 1. 构造待签名字符串。格式非常重要客户端和服务器必须严格一致。 # 通常按固定顺序拼接这些要素。 string_to_sign f{method}\n{path}\n{timestamp}\n{nonce} if body: # 对请求体进行规范化例如排序JSON键或直接使用字符串 if isinstance(body, dict): body_str json.dumps(body, sort_keysTrue, separators(,, :)) else: body_str str(body) string_to_sign f\n{body_str} # 2. 使用HMAC-SHA256进行签名 signature hmac.new(self.secret_key, string_to_sign.encode(utf-8), hashlib.sha256).hexdigest() return signature def make_request(self, method, endpoint, dataNone): url f{self.base_url}{endpoint} timestamp int(time.time()) nonce str(uuid4()) # 随机数确保每次请求的签名都不同 signature self._generate_signature(method, endpoint, timestamp, nonce, data) headers { X-Api-Key: self.api_key, X-Timestamp: str(timestamp), X-Nonce: nonce, X-Signature: signature, Content-Type: application/json } # 服务器端需要验证 # 1. X-Timestamp是否在可接受的时间窗口内如±5分钟防止重放。 # 2. X-Nonce是否在时间窗口内已被使用过需缓存。 # 3. 用X-Api-Key找到对应的secret_key按同样规则计算签名并与X-Signature比对。 if method.upper() GET: resp self.session.get(url, headersheaders, paramsdata) else: resp self.session.post(url, headersheaders, jsondata) return resp # 使用示例 client SignedRequestClient(api_keyyour_key, secret_keyyour_super_secret, base_urlhttps://api.example.com) response client.make_request(POST, /v1/order, data{item: book, qty: 1})4.3 网络层与系统级防护代码层面的防护是最后一道防线更基础的安全应该建立在网络和系统层面。使用VPN或专用网络对于内部服务间的通信确保它们运行在隔离的VPC、子网或通过VPN连接的专有网络中从物理上减少暴露面。防火墙与安全组严格配置入站和出站规则只开放必要的端口。例如数据库服务只允许来自应用服务器的IP访问。定期更新与漏洞扫描保持操作系统、Python解释器、requests、urllib3、cryptography、OpenSSL等所有依赖库的最新版本。使用像bandit、safety这样的安全扫描工具定期检查项目依赖中的已知漏洞。最小权限原则运行Python程序的系统账户应仅拥有完成其功能所必需的最小权限。避免使用root或管理员权限运行应用。5. 实战搭建一个简单的中间人攻击演示环境理解攻击最好的方式就是亲手模拟它请在完全隔离的测试环境如虚拟机中进行。我们将使用mitmproxy这个强大的工具它是一个支持HTTP和HTTPS的中间人代理用Python编写并且提供了Python API非常适合演示和测试。5.1 环境准备与工具安装首先创建一个虚拟环境并安装必要的工具。# 创建并激活虚拟环境 python -m venv mitm-demo source mitm-demo/bin/activate # Linux/macOS # mitm-demo\Scripts\activate # Windows # 安装mitmproxy pip install mitmproxy # 安装requests作为我们的“受害者”客户端 pip install requests5.2 编写一个简单的“受害者”客户端脚本创建一个文件victim_client.py模拟一个不安全的HTTP客户端。# victim_client.py import requests import time def insecure_request(): 模拟一个发送明文敏感信息的请求 url http://httpbin.org/post # 注意是HTTP data { username: test_user, password: SuperSecret123!, # 明文密码 action: login } try: response requests.post(url, datadata, timeout5) print(f[客户端] 请求发送到: {url}) print(f[客户端] 状态码: {response.status_code}) print(f[客户端] 响应体片段: {response.text[:200]}...) return response except requests.exceptions.RequestException as e: print(f[客户端] 请求失败: {e}) return None if __name__ __main__: print(启动不安全的客户端...) for i in range(3): # 发送3次请求 print(f\n--- 第 {i1} 次请求 ---) insecure_request() time.sleep(2)5.3 编写mitmproxy拦截脚本创建一个文件mitm_interceptor.py用于修改经过代理的流量。# mitm_interceptor.py from mitmproxy import http def request(flow: http.HTTPFlow) - None: 拦截并修改请求 # 检查是否是我们的目标请求 if flow.request.pretty_host httpbin.org and flow.request.path.startswith(/post): print(f\n[mitmproxy] 拦截到请求: {flow.request.method} {flow.request.url}) print(f[mitmproxy] 原始请求头: {dict(flow.request.headers)}) # 尝试读取表单数据 if flow.request.urlencoded_form: print([mitmproxy] 原始表单数据:) for key, value in flow.request.urlencoded_form.items(): print(f {key}: {value}) # 演示篡改如果发现密码字段将其修改 if key password: flow.request.urlencoded_form[key] HackedPassword! print(f[mitmproxy] 已将密码字段篡改为: HackedPassword!) # 或者篡改请求头 flow.request.headers[X-Injected-By] MitmProxy-Demo def response(flow: http.HTTPFlow) - None: 拦截并修改响应 if flow.request.pretty_host httpbin.org: # 演示篡改响应体 if flow.response.content: original_content flow.response.content.decode(utf-8, errorsignore) if action: login in original_content: print(f\n[mitmproxy] 拦截到登录响应) # 在响应JSON中注入一个额外的字段 import json try: resp_json json.loads(original_content) resp_json[injected] True resp_json[message] This response was modified by an attacker! flow.response.text json.dumps(resp_json, indent2) print([mitmproxy] 已篡改响应体注入了恶意字段。) except json.JSONDecodeError: pass5.4 运行演示启动mitmproxy在一个终端窗口运行以下命令。它会启动一个代理服务器监听8080端口并加载我们的拦截脚本。mitmweb -s mitm_interceptor.pymitmweb还会启动一个Web界面默认 http://127.0.0.1:8081方便我们直观地查看流量。配置客户端使用代理修改victim_client.py中的请求使其流量经过mitmproxy。# 在victim_client.py的insecure_request函数中修改requests.post调用 proxies { http: http://127.0.0.1:8080, https: http://127.0.0.1:8080, # 注意对于HTTPSmitmproxy需要安装证书这里我们先演示HTTP } response requests.post(url, datadata, timeout5, proxiesproxies, verifyFalse) # 注意verifyFalse仅用于演示由于httpbin.org支持HTTPS为了演示SSL剥离我们可以将URL改为https://httpbin.org/post并在系统或脚本中安装mitmproxy的CA证书mitmproxy启动时会提示证书路径。但为了简化我们先用HTTP演示。运行客户端并观察在另一个终端运行python victim_client.py。你会在mitmproxy的控制台和Web界面中看到请求和响应被拦截、打印甚至篡改的完整过程。客户端收到的响应中会包含我们注入的injected字段。这个演示清晰地展示了在一个不安全的信道HTTP中中间人可以轻松获取你的明文密码并任意篡改你和服务器之间的通信内容。5.5 演示防御生效现在让我们修改客户端应用之前讲到的防御措施。强制HTTPS并验证证书将URL改为https://httpbin.org/post移除proxies参数和verifyFalse。url https://httpbin.org/post response requests.post(url, datadata, timeout5) # 使用默认验证此时如果mitmproxy试图拦截HTTPS请求由于它无法提供由可信CA签发的、针对httpbin.org的证书requests会抛出SSLError连接被终止。这就是证书验证的作用。尝试SSL剥离mitmproxy会尝试将客户端的HTTPS请求降级为HTTP。但因为我们代码中直接写死了HTTPS URL并且没有配置任何降级逻辑客户端会直接向https://...发起连接。mitmproxy的SSL剥离攻击需要配合其他手段如ARP欺骗将客户端的HTTPS请求劫持到自己这里然后它再以HTTP与服务器通信。但对于一个正确编写、直接请求HTTPS且验证证书的客户端这种攻击会失败因为客户端会检测到证书不匹配mitmproxy的证书不是httpbin.org的。通过这个对比演示你可以直观地感受到“使用HTTP且不验证”与“使用HTTPS且严格验证”之间的天壤之别。6. 常见问题排查与安全开发习惯在实际开发中你会遇到各种与安全相关的问题和警告。以下是一些常见场景的排查思路和最佳实践。6.1 常见SSL/TLS错误与解决错误信息可能原因解决方案SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate服务器使用了自签名证书不在系统的受信任CA列表中。开发/测试环境如果信任该服务器可将服务器证书文件下载并在请求时通过verify参数指定其路径。生产环境应使用由公共或内部CA签发的可信证书。SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate服务器证书的中间CA证书在客户端找不到。将服务器提供的完整证书链包括中间CA证书配置到服务器端。客户端可以更新系统的CA证书包或通过verify参数指定包含完整链的证书文件。SSLError: [SSL: UNSUPPORTED_PROTOCOL]或SSLError: [SSL: WRONG_VERSION_NUMBER]客户端与服务器支持的SSL/TLS协议版本不匹配。升级服务器或客户端的TLS版本。在requests中可以配置urllib3的SSL上下文来指定最小/最大版本但需谨慎避免使用不安全的版本。InsecureRequestWarning发出了verifyFalse的请求。永远不要在生产代码中忽略此警告。应解决根本的证书问题而不是屏蔽警告。6.2 安全开发检查清单养成以下习惯能将大部分中间人攻击风险扼杀在摇篮里URL检查代码审查时仔细检查所有网络请求的URL确保生产环境地址都是https://开头。依赖管理使用requirements.txt或Pipfile精确锁定依赖版本定期运行safety check或pip-audit扫描已知漏洞。密钥管理API密钥、密码等绝对不要硬编码在代码中。使用环境变量、密钥管理服务如AWS Secrets Manager, HashiCorp Vault或配置文件并确保配置文件本身不被提交到代码库。# 错误做法 API_KEY hardcoded_secret_key # 正确做法 import os API_KEY os.environ.get(MY_APP_API_KEY) if not API_KEY: raise ValueError(请设置环境变量 MY_APP_API_KEY)日志脱敏确保日志中不会记录完整的请求/响应体、授权头、Cookie等敏感信息。在打印或存储前进行脱敏处理。网络配置在容器或服务器配置中明确设置出站流量的安全策略。例如在Kubernetes的NetworkPolicy中限制Pod只能与特定的服务通信。6.3 针对爬虫开发者的特别提醒爬虫开发者是中间人攻击的高危人群因为经常需要处理各种不规范的网站。谨慎对待verifyFalse很多老旧网站证书有问题开发者会图方便禁用验证。这非常危险。可以考虑折中方案为这些特定的、非敏感的域名单独配置一个不验证的会话并与处理敏感请求的会话隔离。代理安全使用代理IP池时要意识到代理服务器本身就是一个潜在的中间人。尽量避免通过代理发送包含登录态或敏感信息的请求。如果必须使用考虑在客户端与代理之间建立加密通道或者仅将代理用于公开数据的抓取。用户输入即威胁如果你的爬虫配置如起始URL、请求头来自用户输入或配置文件务必进行严格的校验和清洗防止攻击者通过配置将爬虫引导至恶意地址或使其携带恶意载荷。防御中间人攻击是一个持续的过程它贯穿于设计、编码、测试和运维的每一个环节。没有一劳永逸的银弹但通过理解原理、运用正确的工具、并养成良好的安全习惯我们可以极大地提升Python应用在网络通信中的安全性。记住安全的目标不是达到100%的绝对防御而是将攻击的成本提升到远高于其可能获得的收益。