中间人劫持HTTPS,业务强制校验证书禁止insecure跳
中间人劫持HTTPS之所以能成功,绝大多数情况是业务侧主动跳过了证书校验。
只要代码里出现 insecure、verify=False、-k 这类用法,攻击者就能用一张自签证书冒充服务器,在客户端和真实服务端之间建立两条独立的HTTPS通道,窃取或篡改数据。
本文会先说明跳过校验的风险,再给出不同语言环境下的强制校验配置,最后用可执行的方法验证校验是否真正生效。
跳过证书校验等于给中间人开门
HTTPS 的加密依赖证书链验证。
客户端发起请求时,会校验服务器证书是否由受信任的 CA 签发、域名是否匹配、证书是否过期。
一旦跳过这些校验,客户端就会无条件信任任何证书,中间人只需生成一张指向该域名的自签证书,就能完成劫持。
结论:只要允许跳过证书校验,HTTPS 就只剩加密,没有身份认证,数据安全无法保证。 这不是理论风险,而是现实中批量出现的攻击方式。
常见代码里的危险写法自查
先检查现有业务是否出现以下特征:
- Python requests:
verify=False - curl 命令:
-k或--insecure - Node.js https 请求:
rejectUnauthorized: false - Java HttpsURLConnection:自定义
HostnameVerifier直接返回true - 微信支付、支付宝等第三方 SDK 手动关闭校验
这些写法本质上都在告诉客户端“不要验证证书”。
如果是生产环境,必须立刻整改。
不同语言环境下的强制校验配置
Python requests
保持默认的校验行为即可,不需要额外参数,也可以显式指定:
import requests
resp = requests.get("https://api.example.com", verify=True)
如果公司内部使用自建 CA 签发的证书,不要关掉校验,改为指定 CA 文件:
resp = requests.get("https://api.internal.example.com", verify="/etc/ssl/certs/ca.pem")
curl 命令
去掉 -k,让 curl 按系统证书库验证:
curl https://api.example.com
如果需要指定自建 CA:
curl --cacert /etc/ssl/certs/ca.pem https://api.internal.example.com
Node.js
使用内置 https 请求时,保持 rejectUnauthorized 为默认 true:
const https = require('https');
https.get('https://api.example.com', (res) => {});
使用 axios 时不要设置:
// 错误:axios.get(url, { httpsAgent: new https.Agent({ rejectUnauthorized: false }) })
Java
使用 HttpsURLConnection 时,不要自定义跳过校验的 HostnameVerifier。
同时确认 JVM 的 cacerts 里包含目标证书链。
如果需要加载自签证书,用 keytool 导入后正常访问:
keytool -importcert -alias internal-ca -file ca.crt -keystore $JAVA_HOME/lib/security/cacerts
自签名证书不是跳过校验的理由
很多内网或测试环境使用自签名证书,于是开发图省事直接 verify=False。
正确做法是把自签 CA 加入客户端的信任库,而不是关闭校验。
以 Linux 系统为例,将自签 CA 放入系统信任库:
cp ca.crt /usr/local/share/ca-certificates/my-ca.crt
update-ca-certificates
之后 curl、Python、Java 等只要走系统证书库,就能正常完成校验,同时不降低安全性。
验证业务是否真的在校验证书
改完配置后,用以下方法确认强制校验生效:
- 准备一个自签名证书的 HTTPS 服务,例如用 openssl 临时生成;
- 将域名解析指向该服务;
- 用业务代码或 curl 请求该域名;
- 如果请求报错并提示证书无效,说明校验已开启;
- 如果请求成功拿到数据,说明仍然存在跳过校验的代码。
也可以直接请求 https://self-signed.badssl.com/,这个站点使用自签证书。
正常情况下请求应当失败,如果成功则说明客户端没有校验证书。
常见疑问与避坑
为什么内网测试时用 -k 可以,生产环境就不行? 因为内网同样存在被代理劫持的风险。
测试环境可以临时用,但建议尽快改为信任自建 CA,避免把危险习惯带到生产。
证书过期导致业务报错,能临时跳过校验吗? 不能。
正确做法是及时替换新证书,同时做好证书到期监控。
跳过校验会让整个连接失去身份可信度,比报错更麻烦。
修改后需要重启服务吗? 需要。
大部分语言的 TLS 校验配置在进程启动时加载,改完代码或证书后要重启服务,并观察日志确认没有异常。
检测到 insecure 字样的请求,是否一定需要删除? 是。
这类用法相当于把 HTTPS 降级为裸的连接,任何中间人都可以伪造。
你可以在代码仓库里加入关键字扫描,禁止 insecure、verify=False、rejectUnauthorized: false 再次出现。
完成以上配置后,你的业务就真正实现了强制证书校验,中间人劫持HTTPS的路径会被有效封死。
建议顺手检查一遍所有用到的外部 API 请求,尤其是支付、登录和数据同步相关接口,确保没有漏网的跳过校验配置。