> 技术文档 > 如何解决 InsecureRequestWarning: Unverified HTTPS request is being made to host ‘47.113.219.226‘. Adding

如何解决 InsecureRequestWarning: Unverified HTTPS request is being made to host ‘47.113.219.226‘. Adding

如何解决 InsecureRequestWarning: Unverified HTTPS request is being made to host \'47.113.219.226\'. Adding certificate verification is strongly advised. 问题(全网解决方案大全)


目录

  1. 问题描述

  2. 警告产生的原因与背景

  3. 不推荐的“临时”解决办法

    • 3.1. 直接禁止警告输出
    • 3.2. 在单次请求中关闭验证(verify=False
  4. 推荐的“根本”解决办法:证书校验与信任链

    • 4.1. 使用操作系统/Python 自带的信任根存储
    • 4.2. 使用 certifi 库维护信任根
    • 4.3. 自定义并指定 CA 证书文件
    • 4.4. 针对自签名/私有 CA 的证书配置
    • 4.5. 将证书打包到可执行文件或 Docker 镜像中
  5. 多种编程语言/框架下的同类解决思路

    • 5.1. curl 命令行下的 TLS 验证
    • 5.2. Node.js(axiosrequest)下的类似警告
    • 5.3. Java(HttpClientOkHttp)下的 TLS 验证配置
    • 5.4. Go 语言下的 http.Client TLS 配置
    • 5.5. .NET(HttpClient)下的 Server Certificate 验证
  6. 对比与实践:如何从开发到生产确保 HTTPS 安全

    • 6.1. 在本地/测试环境中调试时的最佳实践
    • 6.2. 在 Staging/预发布环境中引入 CA
    • 6.3. 生产环境中的 TLS 证书申请与自动续期流程
  7. 常见故障排查方法与排错技巧

    • 7.1. 使用 openssl s_clientopenssl x509 验证服务器证书
    • 7.2. 检查中间证书链是否完整
    • 7.3. 跨平台时区/系统证书更新导致的问题
    • 7.4. 证书过期、域名/IP 不匹配等常见错误
  8. 总结与建议

  9. 附录:参考链接与资料汇总


问题描述

在使用 Python 的 requests 库(或其底层依赖 urllib3)发起 HTTPS 请求时,若没有对目标主机的 TLS 证书进行校验,就会出现类似如下的警告(Warning):

InsecureRequestWarning: Unverified HTTPS request is being made to host \'47.113.219.226\'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings

它的含义是:你正在向 https://47.113.219.226 发起一个不经过证书验证的 HTTPS 请求。如果对方使用的是自签名证书、或者根本不存在可信的 TLS 证书,就会触发这一警告。简单来说,这是在提醒你:

  • 你的客户端并未验证服务器证书的合法性,存在中间人攻击的风险
  • 建议你在发起请求时启用证书校验机制,以防止安全隐患

这篇技术博客将会“超详细”地汇集来自全网的各种解决方案,从“最急就章”的临时抑制到“根本治本”的正规证书校验,力求读者在开发、测试、生产不同场景下都能找到最合适、最全面的处理方法。


如何解决 InsecureRequestWarning: Unverified HTTPS request is being made to host ‘47.113.219.226‘. Adding


作者简介

猫头虎是谁?

大家好,我是 猫头虎,猫头虎技术团队创始人,也被大家称为猫哥。我目前是COC北京城市开发者社区主理人COC西安城市开发者社区主理人,以及云原生开发者社区主理人,在多个技术领域如云原生、前端、后端、运维和AI都具备丰富经验。

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用方法、前沿科技资讯、产品评测、产品使用体验,以及产品优缺点分析、横向对比、技术沙龙参会体验等。我的分享聚焦于云服务产品评测、AI产品对比、开发板性能测试和技术报告

目前,我活跃在CSDN、51CTO、腾讯云、阿里云开发者社区、知乎、微信公众号、视频号、抖音、B站、小红书等平台,全网粉丝已超过30万。我所有平台的IP名称统一为猫头虎猫头虎技术团队

我希望通过我的分享,帮助大家更好地掌握和使用各种技术产品,提升开发效率与体验。


作者名片 ✍️

  • 博主猫头虎
  • 全网搜索关键词猫头虎
  • 作者微信号Libin9iOak
  • 作者公众号猫头虎技术团队
  • 更新日期2025年06月04日
  • 🌟 欢迎来到猫头虎的博客 — 探索技术的无限可能!

加入我们AI共创团队 🌐

  • 猫头虎AI共创社群矩阵列表
    • 点我进入共创社群矩阵入口
    • 点我进入新矩阵备用链接入口

加入猫头虎的共创圈,一起探索编程世界的无限可能! 🚀

部分专栏链接

🔗 精选专栏

  • 《面试题大全》 — 面试准备的宝典!
  • 《IDEA开发秘籍》 — 提升你的IDEA技能!
  • 《100天精通鸿蒙》 — 从Web/安卓到鸿蒙大师!
  • 《100天精通Golang(基础入门篇)》 — 踏入Go语言世界的第一步!

如何解决 InsecureRequestWarning: Unverified HTTPS request is being made to host ‘47.113.219.226‘. Adding

正文

警告产生的原因与背景

  1. HTTPS 的本质:

    • HTTPS(HTTP over TLS/SSL)在传输层通过加密与身份验证保证通信的机密性与完整性。
    • 其中“验证”(Authentication)就要求客户端验证服务器所呈现的证书是否由受信任的 CA 签发、是否未过期、是否与域名/IP 匹配,以及证书链是否完整。
  2. 为何会出现 InsecureRequestWarning

    • requests 库(或底层的 urllib3)默认会尝试用 certifi 提供的 CA 根证书来验证对方证书。
    • 如果服务器使用自签名证书,或者根本没有提供相应的可信 CA 签发的证书,那么 requests 会因为“证书校验失败”而报错/抛出异常。
    • 为了“方便开发测试”,requests 支持一个参数 verify=False,用于跳过证书验证。但同时也会触发 InsecureRequestWarning 警告,提醒你“这很不安全”。
  3. 风险:中间人攻击(MITM)

    • 如果不验证对方身份,攻击者可以在你与服务器之间插入一个恶意中间人,劫持明文流量或重定向到别处。
    • 即使你认为只是“测试环境”使用 IP 访问,也不该理所应当地关闭验证——随时可能误用到生产环境。
  4. 不同场景对 TLS 验证的需求

    • 开发/调试阶段:快速完成接口调试,往往是使用自签名证书或无证书的测试服务;临时关闭验证可以提高效率,但请勿忽视潜在风险。
    • 本地模拟/测试环境:可以搭建私有 CA,生成测试用证书,并将 CA 根证书导入到本地信任存储以实现完整验证。
    • 预发布/Staging 环境:建议使用与生产相同的证书类型(例如 Let’s Encrypt 或公司自建 PKI),确保和生产环境的 TLS 配置一致。
    • 生产环境:必须使用经公认 CA(例如 Let’s Encrypt、DigiCert、GlobalSign 等)签发的、域名/IP 完全匹配的证书,并配置好自动续期机制。

3. 不推荐的“临时”解决办法

⚠️ 强烈建议仅在开发/测试环境、或者你已经完全了解风险并能够接受时使用下面策略,切勿在生产环境使用。

3.1. 直接禁止警告输出

urllib3InsecureRequestWarning 置为忽略状态。常见做法:

import urllib3urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)import requestsresponse = requests.get(\'https://47.113.219.226/api/xxx\', verify=False)print(response.status_code, response.text)
  • 上述代码对全局生效:所有跳过证书验证的请求均不会再打印 InsecureRequestWarning。

  • 缺点

    • 虚假的安全感:抑制了警告,却仍旧未执行证书验证。
    • 生产中误用风险:容易忘记恢复验证,一旦上线,就等于对所有 HTTPS 请求一律“信任”。

3.2. 在单次请求中关闭验证(verify=False

只针对某一次请求关闭验证,示例:

import requestsresponse = requests.get( \'https://47.113.219.226/api/data\', verify=False # 关闭证书验证,直接忽略警告(若不关闭,仍会抛出 InsecureRequestWarning))print(response.status_code)
  • 这是官方推荐的、用于“临时调试”的方式。

  • 如果不想让警告影响控制台输出,需要配合 urllib3.disable_warnings()(参见 3.1)。

  • 缺点

    • 请求完全不验证服务器身份,易受中间人攻击。
    • 代码可读性差,容易遗漏“哪儿用了 verify=False”,也难以搜寻和统一管理。

4. 推荐的“根本”解决办法:证书校验与信任链

核心思路:始终让客户端对服务器证书进行校验,并且让服务器呈现一个“可信任的、证书链完整的” TLS 证书。本文将分层介绍不同场景下的具体做法。

4.1. 使用操作系统/Python 自带的信任根存储

  • 大部分现代操作系统(例如 Windows、macOS、主流 Linux 发行版)都自带了一个“根 CA 存储(Trust Store)”,其中包含了全球常见公共 CA 的根证书。
  • Python(尤其是使用系统 Python 自带的 ssl 模块)在默认情况下也会去调用系统的信任存储来验证服务器证书,无需额外操作。
4.1.1. 本地已经有受信任的证书时直接使用

假设目标服务器为 https://47.113.219.226,已经绑定了一个由公认 CA(如 Let’s Encrypt、DigiCert)签发的证书,并且 IP 地址与证书中 Subject Alternative Name (SAN) 匹配。那么直接在 Python 中写:

import requestsresponse = requests.get(\'https://47.113.219.226/api/info\')print(response.status_code, response.json())

即可完成证书校验,不需要额外传入 verify 参数;如果服务器证书链完整且根 CA 在操作系统信任库中,就不会出现警告,也不会因证书验证失败而抛出异常。

注意:

  • 当前例子中以 IP 访问(47.113.219.226),但绝大多数 CA 不会给纯 IP 签发公开证书(Let’s Encrypt 也仅支持域名)。
  • 如果只是简单测试 IP 地址对 HTTPS 的访问,很可能需要使用自签名证书,或在证书中把该 IP 写入 SAN 才能正常通过校验。

4.2. 使用 certifi 库维护信任根

  • requests 默认会调用 certifi.where() 所指向的 CA 根证书集合文件。
  • 这种方式下,不依赖机器自带的信任存储,而是依赖 certifi 的独立集合,确保跨平台一致性。
4.2.1. 验证 requests 所使用的 CA 根文件路径
import certifiprint(certifi.where())# 例如输出:/usr/local/lib/python3.9/site-packages/certifi/cacert.pem
  • 当你安装了 requests(通常会一同安装 certifi),默认 requests.get() 会使用 certifi.where() 返回的 .pem 文件来做验证。
4.2.2. 强制指定使用 certifi 的 CA 根

如果想显式地让某个请求使用 certifi 而不是系统的信任库,可以这样写:

import requestsimport certifiresponse = requests.get( \'https://47.113.219.226/api/data\', verify=certifi.where())print(response.status_code)
  • 如果你在一个没有系统 CA 存储的环境(或该环境里的 requests 未启用系统存储),可以考虑此方案。

  • 缺点

    • 如果想使用私有 CA 或自签名 CA,需要把相应 CA 的根证书追加到 certificacert.pem 中,操作相对麻烦。

4.3. 自定义并指定 CA 证书文件

当服务器使用了自签名证书,或者是公司内部 PKI 签发的私有 CA 时,客户端需要拿到该 CA 根证书,然后在请求时显式指定。常见步骤如下。

4.3.1. 从服务器/运维团队获取根证书
  1. 自签名:如果服务器使用的是自签名证书(.crt.pem 格式),请确认服务端将该证书完整地暴露给客户端或通过私有存储分发。

  2. 私有 CA:如果是公司内部 PKI 签发,则需要同时提供“根证书 CA.pem”+可能的“中间证书 intermediate.pem”。

  3. 合并成单一 PEM 文件

    • 如果有根 CA 和中间 CA,可以使用以下命令把它们合并成一个 company_ca_bundle.pem

      cat intermediate.pem root_ca.pem > company_ca_bundle.pem
    • 确保顺序是“中间 CA 在前、根 CA 在后”。

4.3.2. 在请求时指定 verify=CA_BUNDLE_PATH

Python 代码示例:

import requests# 假设 company_ca_bundle.pem 已经放在当前目录CA_BUNDLE_PATH = \'./company_ca_bundle.pem\'response = requests.get( \'https://47.113.219.226/api/secure\', verify=CA_BUNDLE_PATH)print(response.status_code, response.text)
  • 此时,requests 会使用指定的 company_ca_bundle.pem 验证服务器端证书。
  • 如果服务器端证书链正确、证书未过期、IP(或域名)在证书的 SAN 中匹配,则请求成功;否则,会抛出 requests.exceptions.SSLError
4.3.3. 验证过程中的常见错误
  1. certificate verify failed / SSLCertVerificationError

    • 一般是 CA bundle 中缺少了真正签发者的根证书;或顺序颠倒。
    • openssl s_client -connect 47.113.219.226:443 -showcerts 来查看服务端提供的证书链,核对 CA 链是否与本地 company_ca_bundle.pem 完全一致。
  2. IP/域名不匹配

    • 如果访问的是 IP 而证书里只有域名(Common Name 或 SAN)而没有 IP,则会提示“hostname ‘47.113.219.226’ doesn’t match either of ‘example.com’”。

    • 解决:

      • 在生成证书时,将 IP 地址写进 SAN;
      • 或者用域名访问并确保域名指向该 IP;
      • 或者使用 urllib3.disable_warnings()verify=False(仅测试环境)。

4.4. 针对自签名/私有 CA 的证书配置

在企业内部或者本地测试环境,往往会自己搭建私有 CA 并对各服务签发自签名证书。以下是一整套流程示例,涵盖“如何从零开始生成自签名 CA → 签发服务器证书 → 在客户端配置验证”。


4.4.1. 使用 OpenSSL 创建根 CA(仅示例,生产环境需更高安全性)
  1. 生成根 CA 的私钥与自签名根证书

    # 生成根 CA 私钥(2048 位 RSA)并加密(可选密码,这里示例无密码)openssl genrsa -out rootCA.key 2048# 生成根 CA 证书(自签名),有效期10年openssl req -x509 -new -nodes -key rootCA.key \\ -sha256 -days 3650 \\ -subj \"/C=CN/ST=Beijing/L=Beijing/O=MyCompany Ltd./OU=IT/CN=MyCompany Root CA\" \\ -out rootCA.pem
  2. 生成服务器端私钥及证书签发请求(CSR)

    # 生成服务器私钥openssl genrsa -out server.key 2048# 生成 CSR(此处以 IP 举例;如果只用域名,CN 写域名即可,同时要给 SAN 配置 IP)openssl req -new -key server.key \\ -subj \"/C=CN/ST=Beijing/L=Beijing/O=MyCompany Ltd./OU=IT/CN=47.113.219.226\" \\ -out server.csr# 生成一个 v3 配置文件,使证书中包含 Subject Alternative Name(SAN)cat > server_ext.cnf <<EOFauthorityKeyIdentifier=keyid,issuerbasicConstraints=CA:FALSEkeyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEnciphermentsubjectAltName = @alt_names[alt_names]IP.1 = 47.113.219.226DNS.1 = 47.113.219.226# 如果你想同时支持域名访问,可在下面加上# DNS.2 = example.mycompany.comEOF
  3. 使用根 CA 签发服务器证书

    openssl x509 -req -in server.csr \\ -CA rootCA.pem -CAkey rootCA.key -CAcreateserial \\ -out server.crt -days 825 -sha256 \\ -extfile server_ext.cnf
    • rootCA.srl:记录了根 CA 的序列号,用于后续签发。
    • server.crt:由根 CA 签名的服务器证书,可用于部署到 Nginx/Apache 等。
  4. rootCA.pem(根 CA 证书)分发给所有需要信任该证书的客户端

    • 在 Python 客户端,将 rootCA.pem(或与 intermediate.pem 合并后的 bundle.pem)放到某个固定路径。
    • 代码示例与 4.3.2 中介绍的“verify 指定 CA_BUNDLE”相同。
4.4.1.1. 在服务器(如 Nginx)中部署自签名证书示例
server { listen  443 ssl; server_name 47.113.219.226; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; # 如果有中间证书,则使用 cat 合并:cat intermediate.crt >> server.crt # ssl_certificate /etc/nginx/ssl/server_fullchain.crt; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; location / { proxy_pass http://127.0.0.1:8080; # ... 其他代理配置 }}
  • 注意事项

    • 如果只部署了 server.crt,但并没有把中间证书/根证书链一起拼接在一起,就会出现“证书链不完整”导致校验失败。
    • 最好将 server.crt + intermediate.pem + rootCA.pem 合并为 fullchain.pem,在 ssl_certificate 中一起提供。

4.5. 将证书打包到可执行文件或 Docker 镜像中

  • 如果你的客户端是一个打包成可执行文件的工具(例如使用 PyInstaller 打包后的 exe),需要把 CA 根证书或 bundle 一同打包进去。

  • Docker 镜像:

    • 在构建镜像时,将自定义 CA 根证书复制到镜像内系统信任存储(Windows 下添加到证书存储,Linux 下通常复制到 /usr/local/share/ca-certificates/ 并执行 update-ca-certificates)。
4.5.1. 示例:在 Dockerfile 中添加自签名根证书
FROM python:3.9-slim# 将自签名根证书复制到镜像COPY rootCA.pem /usr/local/share/ca-certificates/rootCA.crt# 更新系统信任证书存储RUN apt-get update && \\ apt-get install -y ca-certificates && \\ update-ca-certificates# 安装 Python 依赖COPY requirements.txt /app/RUN pip install --no-cache-dir -r /app/requirements.txt# 复制应用代码COPY . /app/WORKDIR /app/CMD [\"python\", \"app.py\"]

此时,容器内的 Python 程序发起的所有 HTTPS 请求都会默认信任 rootCA.pem


5. 多种编程语言/框架下的同类解决思路

虽然本博客重点关注 Python requests 抛出的 InsecureRequestWarning,但其他语言或工具也会提示类似警告,原理相通:缺少可信 CA 或验证逻辑被关闭。下面列出常见环境下的对策,仅供参考。

5.1. curl 命令行下的 TLS 验证

  • 禁止验证

    curl -k https://47.113.219.226/api/test# 或者等同于 --insecure

    此时 curl 不会验证服务器证书,风险同样存在。

  • 指定 CA 证书

    curl --cacert /path/to/company_ca_bundle.pem https://47.113.219.226/api/test

    如果有中间证书,则同样要把它们“cat”到一起形成一个 bundle。

  • 指定证书目录(将证书放到目录内):

    curl --capath /etc/ssl/company_ca/

    capath 要求该目录下的 CA 证书都必须是 .crt 文件,并且以哈希命名。

  • 验证常见错误

    • curl: (60) SSL certificate problem: unable to get local issuer certificate 通常是缺少中间链/根证书。
    • curl: (51) SSL: certificate subject name ‘47.113.219.226’ does not match target host name ‘example.com’ 则是 IP/域名不匹配。

5.2. Node.js(axiosrequest)下的类似警告

  • axios 默认会继承 Node.js 的 TLS 验证策略。

    • 关闭验证(不推荐,仅测试环境):

      const axios = require(\'axios\');process.env.NODE_TLS_REJECT_UNAUTHORIZED = \'0\';// 或者针对单个请求:axios.get(\'https://47.113.219.226/api/data\', { httpsAgent: new require(\'https\').Agent({ rejectUnauthorized: false }) }) .then(resp => console.log(resp.data)) .catch(err => console.error(err));

      警告:一旦 NODE_TLS_REJECT_UNAUTHORIZED=0,将全局关闭所有 HTTPS 请求的验证,极度不安全。

    • 使用自定义 CA:

      const fs = require(\'fs\');const https = require(\'https\');const axios = require(\'axios\');const ca = fs.readFileSync(\'./company_ca_bundle.pem\');const agent = new https.Agent({ ca: ca, // 用自签名/私有 CA 进行验证 keepAlive: true,});axios.get(\'https://47.113.219.226/api/data\', { httpsAgent: agent }) .then(resp => console.log(resp.data)) .catch(err => console.error(err));
  • request(已废弃,但仍有项目在用)

    • 关闭验证:request({ url: \'https://47.113.219.226\', strictSSL: false }, callback);
    • 指定 CA:request({ url: \'https://47.113.219.226\', ca: fs.readFileSync(\'./company_ca_bundle.pem\') }, callback);

5.3. Java(HttpClientOkHttp)下的 TLS 验证配置

  • Apache HttpClient(4.x)

    • 关闭验证(不推荐,仅测试):

      SSLContext sslContext = SSLContext.getInstance(\"SSL\");sslContext.init(null, new TrustManager[] { new X509TrustManager() { public void checkClientTrusted(X509Certificate[] certs, String authType) {} public void checkServerTrusted(X509Certificate[] certs, String authType) {} public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }}}, new SecureRandom());SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory( sslContext, NoopHostnameVerifier.INSTANCE);CloseableHttpClient httpClient = HttpClients.custom() .setSSLSocketFactory(sslSocketFactory) .build();HttpGet get = new HttpGet(\"https://47.113.219.226/resource\");CloseableHttpResponse resp = httpClient.execute(get);

      上述代码将完全信任任何证书,极其危险

    • 使用自定义 TrustStore

      1. 将根 CA 导入到一个 JKS 或 PKCS12 格式的信任库:

        keytool -importcert -alias mycompanyca -file rootCA.pem -keystore truststore.jks -storepass changeit
      2. 在代码中加载该 truststore.jks

        KeyStore trustStore = KeyStore.getInstance(\"JKS\");try (FileInputStream instream = new FileInputStream(new File(\"truststore.jks\"))) { trustStore.load(instream, \"changeit\".toCharArray());}SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(trustStore, null) .build();SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslContext, new DefaultHostnameVerifier());CloseableHttpClient httpClient = HttpClients.custom() .setSSLSocketFactory(sslsf) .build();
      3. 使用 httpClient 发起请求即可完成 SSL 验证。

  • OkHttp(Square 出品)

    • 关闭验证(仅测试):

      OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(getUnsafeSSLContext().getSocketFactory(), getUnsafeTrustManager()) .hostnameVerifier((hostname, session) -> true) .build();

      getUnsafeSSLContext()getUnsafeTrustManager() 返回一个信任所有证书的 SSLContextTrustManager,同样危险。)

    • 使用自定义 CA:

      CertificateFactory cf = CertificateFactory.getInstance(\"X.509\");InputStream caInput = new FileInputStream(\"company_ca.pem\");Certificate ca = cf.generateCertificate(caInput);caInput.close();KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());keyStore.load(null, null);keyStore.setCertificateEntry(\"ca\", ca);TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm());tmf.init(keyStore);SSLContext sslContext = SSLContext.getInstance(\"TLS\");sslContext.init(null, tmf.getTrustManagers(), null);OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager)tmf.getTrustManagers()[0]) .build();

5.4. Go 语言下的 http.Client TLS 配置

  • Go 默认会使用系统根证书来验证,如果服务器证书有效,一般不需要额外配置。

  • 关闭验证(仅测试):

    tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true},}client := &http.Client{Transport: tr}resp, err := client.Get(\"https://47.113.219.226/api\")
  • 加载自定义根证书:

    caCert, err := ioutil.ReadFile(\"company_ca.pem\")if err != nil { log.Fatalf(\"Unable to read CA cert: %v\", err)}caCertPool := x509.NewCertPool()caCertPool.AppendCertsFromPEM(caCert)tlsConfig := &tls.Config{ RootCAs: caCertPool,}transport := &http.Transport{TLSClientConfig: tlsConfig}client := &http.Client{Transport: transport}resp, err := client.Get(\"https://47.113.219.226/api\")

5.5. .NET(HttpClient)下的 Server Certificate 验证

  • 关闭验证(仅测试):

    var handler = new HttpClientHandler();handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;var client = new HttpClient(handler);var response = await client.GetAsync(\"https://47.113.219.226/api\");
  • 使用自定义根证书:

    1. 将根证书以 PEM/DER 格式加载,转换为 X509Certificate2

      X509Certificate2 caCert = new X509Certificate2(\"company_ca.cer\");var handler = new HttpClientHandler();handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>{ // 在链的根位置插入自定义 CA chain.ChainPolicy.ExtraStore.Add(caCert); chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; bool isValid = chain.Build(cert); // 也可以检查 cert.Subject 是否匹配或检查 cert.Issuer 匹配 return isValid;};var client = new HttpClient(handler);var response = await client.GetAsync(\"https://47.113.219.226/api\");

6. 对比与实践:如何从开发到生产确保 HTTPS 安全

在不同环境切换时,往往会使用不同程度的“放宽”或严格的 TLS 验证策略。以下结合实践,给出一个由浅入深的最佳实践流程。

6.1. 在本地/测试环境中调试时的最佳实践

  1. 原则:尽量保持与生产环境一致的验证流程。

  2. 推荐做法:搭建一个由“私有 CA”签发的测试证书

    • 生成一个私有根 CA 并导入到本地系统信任存储(或者在 certifi 中追加、或通过 Python 指定 verify)。
    • 对测试服务器签发证书,确保在本地访问时不会出现“证书验证失败”。
    • 这样既能“用 HTTPS”,又能避免关闭验证所带来的安全隐患。
  3. 如果确实需要临时关闭验证

    • 一定要在代码中显著标注:例如在 request 前后写好注释 # TODO: 仅测试环境关闭验证,发布请移除

    • 不要直接修改全局抑制;尽量用内联 verify=False 限定作用域:

      # NOTE: 本行仅在 dev 环境下关闭验证,prod 环境需要改为 verify=\'/path/to/ca.pem\'response = requests.get(url, verify=False)
  4. 始终保持私有 CA 的私钥/根证书文件在版本管理之外,不要泄露。

6.2. 在 Staging/预发布环境中引入 CA

  1. 原则:Staging 环境尽量使用可公开访问的证书,如 Let’s Encrypt、或者已导入业务私有 CA(与生产环境相同或同级别 PKI)。

  2. 如果使用 Let’s Encrypt:

    • 绑定一个二级域名(例如 staging.example.com),让 DNS 指向测试服务器 IP。
    • 使用 certbot 申请 Let’s Encrypt 证书,自动续期。
  3. 如果是私有 CA:

    • 将私有根 CA 导入到 Staging 环境操作系统信任存储。
    • 在运行时,客户端同样使用系统信任库(比如 requests 默认即可)来校验证书。
  4. 这么做的好处:Staging 与生产环境做“真实模拟”,一旦证书链或续期脚本出现问题,能及早发现。

6.3. 生产环境中的 TLS 证书申请与自动续期流程

  1. Let’s Encrypt/ACME 自动化

    • 使用 certbot(或其它 ACME 客户端)自动申请和续期。

    • 在 Nginx/Apache/Traefik/HAProxy 等反向代理中配置 自动热加载

    • 流程示例:

      1. certbot certonly --webroot -w /var/www/html -d example.com
      2. 在 Cron 中执行 certbot renew --post-hook \"systemctl reload nginx\"
    • 让 HTTPS 证书自动化,降低人为失误。

  2. 企业/私有 CA 集成

    • 搭建一个内部 PKI:使用 EJBCA、OpenCA、HashiCorp Vault PKI、微软 ADCS 等。
    • 定期签发并下发“中间 CA 证书 + 工作证书(用于各服务)”,并将“中间 CA”加入操作系统信任存储或通过集中化配置管理分发。
    • 生产环境强制客户端(包括人、应用)均使用操作系统/语言环境自带的信任存储,减少手动指定 verify= 的麻烦。
  3. 统一监控与预警

    • 建议对证书到期时间建立监控告警:

      • 可以在 Prometheus + Blackbox Exporter 中检测 https://example.com 的证书有效期;
      • 或者使用外部服务(如 Let’s Monitor、SSL Labs API)定期获取证书过期日期,提前提醒运维同学。
  4. 合理配置客户端超时时间与重试策略

    • 为网络请求设置合理的超时时间(timeout=5 秒之类),避免请求长期挂起。
    • 如果客户端使用了负载均衡或多节点集群,对链路不稳定或证书更新时短暂失效的情况,要有重试或快速退避(Exponential Backoff)机制,避免大量 5xx 错误影响业务。

7. 常见故障排查方法与排错技巧

在排查 InsecureRequestWarning 及其它 TLS/SSL 相关错误时,可以参考以下思路和工具。

7.1. 使用 openssl s_clientopenssl x509 验证服务器证书

  1. 检查服务器收益到的证书链

    openssl s_client -connect 47.113.219.226:443 -showcerts
    • If the server sends only its leaf certificate but没有中间 CA,则会看到类似:

      depth=0 CN = 47.113.219.226verify error:num=20:unable to get local issuer certificate
    • 你需要等到输出完整链信息后,确保包含“中间证书”与“根证书”。

  2. 提取并转换服务器的 Leaf Certificate

    openssl s_client -connect 47.113.219.226:443 -showcerts </dev/null 2>/dev/null \\ | openssl x509 -outform PEM > server_leaf.pem
    • 然后可以进一步:

      openssl x509 -in server_leaf.pem -text -noout

      查看证书有效期、颁发者(Issuer)、签发者(Subject)、SAN 列表等信息。

    • 特别关注:

      • Not Before / Not After:证书是否过期
      • Issuer 是否为你所信任的 CA
      • Subject Alternative Name 是否包含 IP 或域名
  3. 验证本地手头的 CA 文件是否能验证服务器证书

    openssl verify -CAfile company_ca_bundle.pem server_leaf.pem
    • 如果输出 server_leaf.pem: OK,说明用这个 CA bundle 能通过对 Leaf 证书的验证。
    • 否则输出错误提示(如 unable to get local issuer certificate),说明你本地缺少上游某个中间证书。

7.2. 检查中间证书链是否完整

  • 大多数生产环境错在“只部署了 Leaf+Intermediate1,但缺少了 Intermediate2 或 根 CA”。

  • 最终验证流程是:“Leaf → Intermediate → … → Root CA → Trust Store”,任何一环断裂都会验证失败。

  • 建议:

    1. 让服务器运维人员提供完整链(通常称为 fullchain.pem)。
    2. openssl s_client 确认链路。

7.3. 跨平台时区/系统证书更新导致的问题

  • 某些 Linux 发行版旧版本的 ca-certificates 包已过期,可能缺少新的根 CA;即使在本地机器能通过,切换到 CI 环境就可能“找不到根 CA”。
  • 如果用 certifi,可以通过 pip install --upgrade certifi 来同步到最新的根 CA 列表。
  • Windows 上某些自签名 CA 导入方式:需要在“受信任的根证书颁发机构” -> “证书” 中手动导入 .pem → 生效。

7.4. 证书过期、域名/IP 不匹配等常见错误

  1. 证书过期

    • 客户端会提示 certificate has expired
    • 需要将服务器上的旧证书替换成新证书(Let’s Encrypt 自动续期,或手动签发更换)。
  2. 主机名(IP)与证书不匹配

    • 常见报错:hostname \"47.113.219.226\" does not match \"example.com\"

    • 解决:

      • 在证书签发时将 IP 写进 SAN(仅自签名/私有 CA 支持)。
      • 或者改为用域名访问,并确保 DNS 解析到正确的 IP。
      • 如果你执意用 IP 访问,所有客户端均需要在 verify 时额外提供一个“忽略 host 验证”的回调(例如 Node.js 里可写 .hostnameVerifier(() => true),但强烈不推荐在生产这样做)。
  3. 中间证书缺失

    • 如前文所述,部署时要把中间 CA 拼接到 server 端配置中。
    • 如果 “openssl s_client” 输出显示链不完整,则立刻修复。

8. 总结与建议

  • 绝不在生产环境中使用 verify=False 或者全局禁用 InsecureRequestWarning
  • 强烈推荐从开发早期就使用“私有 CA + 本地信任” 的模式,做到开发/测试 与 生产“TLS 验证”流程的无缝衔接。
  • Let’s Encrypt 等公共 CA 是最常见的免费签发方式,但它们对“纯 IP 证书”不支持;使用它们时请务必绑定域名。
  • 涉及到 IP HTTPS 访问的场景,最常用的方式是“自签名 CA + SAN 中加入 IP”,然后让客户端一并信任该自签名 CA。
  • 务必检查证书链的完整性:Leaf → 一或多级中间 CA → Root CA → Trust Store。
  • 在代码层面,务必做到“即便是测试环境也要显式标注/文档化关闭验证的地方”,提高可维护性,防止误部署。
  • 自动化证书续期与监控预警不可或缺:让你的业务不至于因为证书到期而“全网告警”,也能及时发现链路中断导致的证书校验故障。

9. 附录:参考链接与资料汇总

  1. Python requests 文档:Advanced Usage / SSL Warnings

    • https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  2. certifi 官方仓库与说明

    • https://github.com/certifi/python-certifi
  3. OpenSSL 常用命令手册

    • man openssl / https://www.openssl.org/docs/man1.1.1/
  4. Let’s Encrypt 官方文档

    • https://letsencrypt.org/docs/
  5. Node.js TLS/SSL 支持文档

    • https://nodejs.org/api/tls.html
  6. Apache HttpClient SSL/TLS 配置

    • https://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e217
  7. OkHttp TLS 教程

    • https://square.github.io/okhttp/ssl/
  8. Go 标准库 crypto/tls 文档

    • https://pkg.go.dev/crypto/tls
  9. .NET HttpClient HTTPS 配置示例

    • https://docs.microsoft.com/dotnet/api/system.net.http.httpclienthandler.servercertificatecustomvalidationcallback

本文涵盖了从“最简单的抑制警告”到“企业级 TLS 证书管理”的整套思路与实践细节。希望对你研发、测试、运维各环节都能有实质帮助,让你的 HTTPS 通信既符合安全规范,又在开发效率与维护成本之间取得平衡。祝顺利解决 InsecureRequestWarning: Unverified HTTPS request

猫头虎

粉丝福利


👉 更多信息:有任何疑问或者需要进一步探讨的内容,欢迎点击文末名片获取更多信息。我是猫头虎博主,期待与您的交流! 🦉💬
如何解决 InsecureRequestWarning: Unverified HTTPS request is being made to host ‘47.113.219.226‘. Adding


联系我与版权声明 📩

  • 联系方式
    • 微信: Libin9iOak
    • 公众号: 猫头虎技术团队
  • 版权声明
    本文为原创文章,版权归作者所有。未经许可,禁止转载。更多内容请访问猫头虎的博客首页。

点击✨⬇️下方名片⬇️✨,加入猫头虎AI共创社群矩阵。一起探索科技的未来,共同成长。🚀

🔗 猫头虎抱团AI共创社群 | 🔗 Go语言VIP专栏 | 🔗 GitHub 代码仓库 | 🔗 Go生态洞察专栏 ✨ 猫头虎精品博文

在这里插入图片描述