Slack WebClient经代理仍无法跳过证书验证问题求助
在Web服务的TypeScript REST接口中,我需要创建通过mitmproxy代理访问Slack API的Slack WebClient。代码里已经给代理Agent设置了rejectUnauthorized: false来跳过证书验证,但还是出现证书验证失败的错误。客户端日志显示“unable to verify the first certificate”,mitmproxy日志提示客户端不信任代理证书。HTTPS_PROXY环境变量设为https://localhost:8080(mitmproxy监听地址)。全局设置NODE_TLS_REJECT_UNAUTHORIZED=0能解决问题,但这是不良实践,请问如何在通过代理访问时局部跳过Slack的证书验证?
现有代码
private createProxyAwareSlackClient(token: any) { const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY; const slackClientOptions = { logLevel: LogLevel.DEBUG }; if (proxyUrl) { slackClientOptions['agent'] = new URL(proxyUrl).protocol === 'http' ? new HttpProxyAgent(proxyUrl, {rejectUnauthorized: false}) : new HttpsProxyAgent(proxyUrl, {rejectUnauthorized: false}); } return new WebClient(token, slackClientOptions); } async firstHandshake(settings: any) { const slackWebClient = this.createProxyAwareSlackClient(undefined); let response: WebAPICallResult; try { response = await slackWebClient.oauth.v2.access({ client_id: process.env.SLACK_CLIENT_ID, client_secret: process.env.SLACK_CLIENT_SECRET, code: settings.slack_code, redirect_uri: settings.redirect_uri }); } catch (e) { logger.error(e); throw createError(500, 'Cannot get slack api key'); }
客户端错误日志
[WARN] web-api:WebClient:8 http request failed unable to verify the first certificate [DEBUG] web-api:WebClient:8 http request url: https://slack.com/api/oauth.v2.access [DEBUG] web-api:WebClient:8 http request body: {"client_id":"id","client_secret":"secret","code":"some_code","redirect_uri":"some_url"} [DEBUG] web-api:WebClient:8 http request headers: {} [WARN] web-api:WebClient:8 http request failed unable to verify the first certificate
mitmproxy日志
[23:02:09.731][127.0.0.1:59940] client connect [23:02:09.826][127.0.0.1:59940] server connect slack.com:443 (some_ip:443) [23:02:09.911][127.0.0.1:59940] Client TLS handshake failed. The client disconnected during the handshake. If this happens consistently for slack.com, this may indicate that the client does not trust the proxy's certificate. [23:02:09.913][127.0.0.1:59940] client disconnect [23:02:09.914][127.0.0.1:59940] server disconnect slack.com:443 (some_ip:443)
问题核心:你给代理Agent设置的rejectUnauthorized只控制客户端到代理的TLS验证,但Slack WebClient在处理代理返回的Slack服务证书时,还会走自身的TLS验证逻辑,这部分没被跳过。下面是几种局部解决的方法:
方法1:给Slack WebClient添加专属TLS配置
直接在Slack客户端的选项里添加tls字段,设置rejectUnauthorized: false,这个配置只会作用于当前WebClient实例,不会影响全局:
private createProxyAwareSlackClient(token: any) { const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY; const slackClientOptions = { logLevel: LogLevel.DEBUG, // 仅当前Slack客户端生效的TLS配置 tls: { rejectUnauthorized: false } }; if (proxyUrl) { slackClientOptions['agent'] = new URL(proxyUrl).protocol === 'http' ? new HttpProxyAgent(proxyUrl, {rejectUnauthorized: false}) : new HttpsProxyAgent(proxyUrl, {rejectUnauthorized: false}); } return new WebClient(token, slackClientOptions); }
方法2:确保代理Agent的配置覆盖全链路验证
部分情况下,Slack WebClient会优先使用Agent的配置而不是自身的tls选项,此时可以统一给Agent传递验证配置:
private createProxyAwareSlackClient(token: any) { const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY; const slackClientOptions = { logLevel: LogLevel.DEBUG }; if (proxyUrl) { const agentOptions = { rejectUnauthorized: false }; slackClientOptions['agent'] = new URL(proxyUrl).protocol === 'http' ? new HttpProxyAgent(proxyUrl, agentOptions) : new HttpsProxyAgent(proxyUrl, agentOptions); } return new WebClient(token, slackClientOptions); }
更安全的替代方案(推荐)
跳过证书验证存在安全风险,更稳妥的方式是将mitmproxy的CA证书加入信任列表,仅让当前Slack客户端信任该证书:
import * as fs from 'fs'; private createProxyAwareSlackClient(token: any) { const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY; // 替换为你的mitmproxy CA证书路径 const mitmCaCert = fs.readFileSync('./mitmproxy-ca.pem'); const slackClientOptions = { logLevel: LogLevel.DEBUG, tls: { ca: mitmCaCert } }; if (proxyUrl) { slackClientOptions['agent'] = new URL(proxyUrl).protocol === 'http' ? new HttpProxyAgent(proxyUrl) : new HttpsProxyAgent(proxyUrl, { ca: mitmCaCert }); } return new WebClient(token, slackClientOptions); }
内容的提问来源于stack exchange,提问作者rokpoto.com

