You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于http-mitm-proxy的MITM代理因自签名证书通信失败求助

解决MITM代理与自签名HTTPS服务器的证书信任问题

看起来你在MITM代理的证书逻辑和服务器信任配置上踩了几个典型的坑,我来一步步帮你梳理并解决:

核心错误分析

你的问题主要出在两个关键环节:

  1. 代理连接目标HTTPS服务器时的证书验证:Node.js默认会拒绝自签名证书,所以代理作为客户端连接你的HTTPS文件服务器时,抛出了DEPTH_ZERO_SELF_SIGNED_CERT错误。
  2. MITM代理的证书签发逻辑:你直接复用了目标服务器的证书给客户端,这不符合中间人攻击的原理——代理应该用自己的根CA动态签发目标域名的证书,让客户端信任这个根CA即可。

步骤1:修复代理对目标服务器的证书信任问题

代理作为客户端连接你的自签名HTTPS服务器时,需要跳过证书验证(测试环境)或者添加信任的根CA(生产环境)。在http-mitm-proxy的onRequest钩子中修改请求配置:

proxy.onRequest(function(ctx, callback) {
  console.log('onRequest');
  // 测试环境:禁用目标服务器的证书验证(快速解决)
  ctx.proxyToServerRequestOptions.rejectUnauthorized = false;
  // 生产环境:添加目标服务器的根CA到信任列表
  // ctx.proxyToServerRequestOptions.ca = fs.readFileSync('目标服务器的根CA.crt');
  callback();
});

步骤2:重构MITM代理的证书体系

你需要生成一个独立的MITM根CA,用来动态签发目标域名的证书,而不是复用目标服务器的证书:

1. 生成MITM根CA(用OpenSSL命令)

# 生成根CA私钥
openssl genrsa -out mitm-ca.key 2048
# 生成自签名根CA证书(有效期10年)
openssl req -x509 -new -nodes -key mitm-ca.key -sha256 -days 3650 -out mitm-ca.crt

2. 修改代理代码,使用MITM根CA

移除你自定义的onCertificateRequired钩子(http-mitm-proxy会自动用根CA动态生成证书),并在启动时指定根CA的密钥和证书:

// 移除原来的proxy.onCertificateRequired钩子

// 启动代理时配置MITM根CA
proxy.listen({ 
  port: port, 
  sslCaKey: path.resolve('mitm-ca.key'),
  sslCaCert: path.resolve('mitm-ca.crt')
});

3. 在Firefox中导入正确的证书

把刚才生成的mitm-ca.crt导入Firefox的信任证书库(而不是目标服务器的证书),这样Firefox会信任代理签发的所有证书。


步骤3:确认域名与证书的一致性

确保:

  • 目标HTTPS服务器的证书CN(通用名称)或SAN(主题备用名称)和你在Hosts中配置的域名(比如myupdateproxy.com)完全匹配
  • 客户端访问的域名和Hosts映射的IP一致,这样代理生成的证书域名才能匹配

最终修正后的代理代码

'use strict';
var port = 8081;
var path = require('path');
var Proxy = require('http-mitm-proxy');
var proxy = Proxy();
const fs = require('fs');
var chunks1 = [];
var chunks2 = [];

proxy.onError(function(ctx, err, errorKind) {
  var url = (ctx && ctx.clientToProxyRequest) ? ctx.clientToProxyRequest.url : '';
  console.error(errorKind + ' on ' + url + ':', err);
});

proxy.onRequest(function(ctx, callback) {
  console.log('onRequest');
  // 禁用目标服务器证书验证(测试用)
  ctx.proxyToServerRequestOptions.rejectUnauthorized = false;
  callback();
});

proxy.onRequestData(function(ctx, chunk, callback) {
  console.log('onRequestData');
  chunks1.push(chunk);
  callback(null, chunk);
});

proxy.onRequestEnd(function(ctx, callback) {
  if (ctx.clientToProxyRequest.socket.remoteAddress !== undefined && ctx.proxyToServerRequest.socket.remoteAddress !== undefined && (Buffer.concat(chunks1)).length > 0) {
    console.log('From: ' + ctx.clientToProxyRequest.socket.remoteAddress);
    console.log('To: ' + ctx.proxyToServerRequest.socket.remoteAddress);
    console.log('Size: ' + (Buffer.concat(chunks1)).length);
    console.log('');
  }
  chunks1 = [];
  callback();
});

proxy.onResponse(function(ctx, callback) {
  callback(null);
});

proxy.onResponseData(function(ctx, chunk, callback) {
  chunks2.push(chunk);
  callback(null, chunk);
});

proxy.onResponseEnd(function(ctx, callback) {
  var total_size=(Buffer.concat(chunks2)).length;
  if (ctx.serverToProxyResponse.socket.remoteAddress !== undefined && ctx.proxyToClientResponse.socket.remoteAddress !== undefined && total_size > 0) {
    console.log('From: ' + ctx.serverToProxyResponse.socket.remoteAddress);
    console.log('To: ' + ctx.proxyToClientResponse.socket.remoteAddress);
    console.log('Size: ' + total_size);
    console.log('');
  }
  console.log((Buffer.concat(chunks2)).toString());
  chunks2 = [];
  callback();
});

// 使用MITM根CA启动代理
proxy.listen({ 
  port: port, 
  sslCaKey: path.resolve('mitm-ca.key'),
  sslCaCert: path.resolve('mitm-ca.crt')
});
console.log('listening on ' + port);

总结

你的核心问题是混淆了"目标服务器的自签名证书"和"MITM代理的根CA证书"的作用:

  • 目标服务器的证书用来让代理信任它
  • MITM代理的根CA用来让客户端信任代理签发的证书

按照上面的步骤调整后,代理就能正常拦截、解密HTTPS流量了。

内容的提问来源于stack exchange,提问作者conectionist

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 18:54:09