Axios 的代理问题探究

admin 2025年1月11日12:39:39评论7 views字数 10221阅读34分4秒阅读模式

愿灾区人民平安

0x01 简介

大家好,我们是 NOP Team ,今天和大家讨论一下关于 node.js 和 Electron App中 axios 代理的问题

Axios 是一个基于 promise 的网络请求库,可以用于浏览器和 node.js ,是 Node.js 中使用范围最广泛的网络库之一,甚至没有之一,我们今天针对 axios 的代理问题来进行一些测试,解决一些长期以来的困惑

本篇文章部分配置在 MacOS 下进行,Windows、Linux 可能有些不同

0x02 代理情况分类

我们抛开代理层级以及外部程序因素,仅从我们自己的程序使用代理的情况来说,分为以下四种情况

  • 无代理
  • 环境变量代理
  • 系统代理
  • 自定义代理

1. 无代理

这里说的无代理是指不使用任何代理,包括环境变量代理、系统代理,但是如果遇到类似 Proxychains 这类的辅助代理程序,那还是会遵从辅助代理程序的结果

2. 环境变量代理

环境变量代理是指通过设置以下环境变量设置的代理,主要针对通过终端执行的程序,当然部分程序也会手动读取环境变量后使用相关代理

export HTTP_PROXY=http://username:password@host:port
export HTTPS_PROXY=http://username:password@host:port
export no_proxy=localhost,127.0.0.*

3. 系统代理

系统代理是指系统层面的代理,默认大部分程序都会遵从系统代理,当然也会有一些特例,例如 Chrome

4. 自定义代理

这个不需要过多解释,大家都理解

0x03 测试代理使用情况

接下来我们测试一下 Node.js 环境下使用 axios 使用上述代理的情况

以以下两个网站为例,代理以本地 http://127.0.0.1:1080 为例

https://api.ipify.org?format=json  // 无法直接访问
https://myip.ipip.net/ // 可直接访问

1. 无代理

const axios = require("axios");

// IP APIs
const IPIFY_URL = "https://api.ipify.org?format=json"// ipify API
const IPIP_URL = "https://myip.ipip.net/"// ipip.net API

// Function to fetch IP using Axios
const fetchIP = async (url, config = {}) => {
    try {
        const response = await axios.get(url, config);
        console.log("Response:", response.data);
    } catch (error) {
        console.error("Error:", error.code);
    }
}


const testProxy = async () => {
    let response;

    console.log('1. 测试无代理情况下 ipify 访问情况')
    response = await fetchIP(IPIFY_URL, {
        proxyfalse
    })

    console.log('2. 测试无代理情况下 ipip.net 访问情况')
        response = await fetchIP(IPIP_URL, {
            proxyfalse
        })

}

testProxy()

需要注意的是,不使用桌面代理的情况下,需要像上面一样设置 proxy: false

Axios 的代理问题探究

从这里可以看出一个问题,在默认情况下,axios 是不走系统代理的,我们看一下当前系统代理设置情况

Axios 的代理问题探究

系统代理配置没有问题,但是默认 axios 就是不走,即使我们把 proxy: false 取消也是一样的

Axios 的代理问题探究

这也意味着一会儿我们的系统代理无法测试,因为它根本就不走

2. 环境变量代理

我们在终端中设置以下内容

export HTTP_PROXY=http://127.0.0.1:1080
export HTTPS_PROXY=http://127.0.0.1:1080

测试代码如下

const axios = require("axios");

// IP APIs
const IPIFY_URL = "https://api.ipify.org?format=json"// ipify API
const IPIP_URL = "https://myip.ipip.net/"// ipip.net API

// Function to fetch IP using Axios
const fetchIP = async (url, config = {}) => {
    try {
        const response = await axios.get(url, config);
        console.log("Response:", response.data);
    } catch (error) {
        console.error("Error:", error.code);
    }
}


const testProxy = async () => {
    let response;

    console.log('1. 测试环境变量代理情况下 ipify 访问情况')
    response = await fetchIP(IPIFY_URL, {
        // proxy: false
    })

    console.log('2. 测试环境变量代理情况下 ipip.net 访问情况')
        response = await fetchIP(IPIP_URL, {
            // proxy: false
        })
}

testProxy()

1) MacOS

Axios 的代理问题探究

可以看到,此时访问 ipify 成功了,之所以两个网站返回的内容不一样是因为做了分流

这里有一个疑问,我们开发的程序是 App ,它是直接点击执行的,默认会走环境变量代理吗?

正好可以用我们的 IP-Recoder 来进行测试,因为 IP-Recorder 中标记的系统代理其实就是环境变量代理

我们看一下 src/utils/proxyWapper.mjs 文件

import { HttpProxyAgent } from 'http-proxy-agent'
import { HttpsProxyAgent } from 'https-proxy-agent'
import { SocksProxyAgent } from 'socks-proxy-agent'

export function createAxiosConfig(proxyType, customProxyConfig = null{
    const baseConfig = {
        timeout5000,
        maxRedirects5
    }

    switch (proxyType) {
        case 0// 无代理
            return {
                ...baseConfig,
                proxyfalse,
                httpAgentnull,
                httpsAgentnull
            }

        case 1// 系统代理
            return {
                ...baseConfig
            }

        case 2// 自定义代理
            if (!customProxyConfig) {
                throw new Error('Custom proxy config is required')
            }

            const { protocol, host, port, auth } = customProxyConfig
            let agent

            // 构建代理URL
            const getProxyUrl = () => {
                const proto = protocol === 0 ? 'socks5' : protocol === 1 ? 'http' : 'https'

                if (auth && auth.username && auth.password) {
                    return `${proto}://${auth.username}:${auth.password}@${host}:${port}`
                }
                return `${proto}://${host}:${port}`
            }

            // 创建对应的agent
            if (protocol === 0) {
                agent = new SocksProxyAgent(getProxyUrl())
            } else if (protocol === 1) {
                agent = new HttpProxyAgent(getProxyUrl())
            } else {
                agent = new HttpsProxyAgent(getProxyUrl())
            }

            return {
                ...baseConfig,
                httpAgent: agent,
                httpsAgent: agent,
                proxyundefined // 关键:使用agent时不设置proxy
            }

        default:
            throw new Error('Invalid proxy type')
    }
}

其中的 case 1 ,也就是系统代理处直接返回了默认配置,从上面的测试我们知道,这并不是系统代理,很多文章说的都是错的,这就是环境变量代理,所以正好我们来测试

我们直接在shell的配置文件中放入

export HTTP_PROXY=http://127.0.0.1:1080
export HTTPS_PROXY=http://127.0.0.1:1080
Axios 的代理问题探究
Axios 的代理问题探究
Axios 的代理问题探究
Axios 的代理问题探究

由于测试环境使用 fish 为默认的shell,所以还在 fish 的配置文件中写入了

set -x HTTP_PROXY http://127.0.0.1:1080
set -x HTTPS_PROXY http://127.0.0.1:1080
Axios 的代理问题探究

退出登录后,打开终端验证代理情况,打开 IP-Recorder,测试系统代理是否可用

Axios 的代理问题探究
Axios 的代理问题探究

这说明在 MacOS 中,通过图标点击启动的 App 是不走通过 export HTTP(S)_PROXY 设置的环境变量代理的

但是,MacOS 中有 launchctl 的设置方式,可以让 GUI 走环境变量代理,在此之前,我们得清除掉之前设置的代理,退出登录,再次登录进行配置

launchctl setenv HTTP_PROXY http://127.0.0.1:1080
launchctl setenv HTTPS_PROXY http://127.0.0.1:1080
launchctl setenv NO_PROXY localhost,127.0.0.1
Axios 的代理问题探究
Axios 的代理问题探究
Axios 的代理问题探究
Axios 的代理问题探究

可以看到,通过 launchctl setenv 这种方式设置的环境变量代理默认App是可以显式地使用的

2) Linux

接下来测试一下在 Linux 桌面环境中的情况,以 Ubuntu Desktop 24.04 为例

Axios 的代理问题探究
Axios 的代理问题探究
Axios 的代理问题探究

可以看到,在 Ubuntu Desktop 24.04 中,当使用默认配置时,App 程序中使用的 axios 是会走环境变量代理的

使用之前的脚本测试一下

配置环境变量代理后

Axios 的代理问题探究

可以看到,配置了环境变量代理后,脚本中的 axios 默认会使用环境变量代理

3) Windows

接下来我们测试一下 Windows,以 Windows 11 为例

还是先使用之前的脚本

未配置环境变量代理时

Axios 的代理问题探究

接下来配置环境变量

Axios 的代理问题探究
Axios 的代理问题探究

配置环境变量后,默认情况下脚本可以使用环境变量代理

在测试命令行启动的应用后,还使用打包好的 App 程序进行了测试

配置环境变量代理前

Axios 的代理问题探究
Axios 的代理问题探究

配置环境变量代理后

Axios 的代理问题探究
Axios 的代理问题探究

可以看到, App 程序中的 axios 默认配置会使用环境变量代理

3. 自定义代理

这部分就不测试了,比较简单

4. 系统代理

系统代理区别于环境变量代理的是,它是系统级的,配置以后,绝大多数软件都会使用该代理,当然也有例外,例如 Chrome

部分资料显示, axios 是不支持系统代理的,我们挨个测试一下,清除之前设置的环境变量代理,参数保持默认和 {proxy: false} ,看看这两种情况下,会不会响应系统代理

1) MacOS

Axios 的代理问题探究
Axios 的代理问题探究
Axios 的代理问题探究
Axios 的代理问题探究

可以看到,默认情况下,无论是命令行执行的 axios 还是 App 点击执行中的 axios 都是不会使用系统代理的

如果想获取系统代理,只能通过执行系统命令来获取了(这部分系统命令还挺闹心的,复杂的点在于用户可能更改服务名字,比如我就改了,导致过程变得复杂,但为了兼容性考虑,必须这么做)

const { execSync } = require('child_process');

function getMacNetworkServices() {
  try {
    // 获取网络服务顺序
    const serviceOrder = execSync('networksetup -listnetworkserviceorder | grep -v "An asteris"')
      .toString()
      .split('nn')
      .filter(Boolean);

    // 获取硬件端口列表用于匹配
    const hardwarePorts = execSync('networksetup -listallhardwareports')
      .toString()
      .split('nn')
      .filter(Boolean);

    // 过滤掉说明行和VLAN配置
    const connectedPorts = hardwarePorts.filter(port => 
      !port.includes('VLAN') && 
      !port.startsWith('An asterisk') &&
      port.includes('Device:')
    );

    const activeServices = [];

    // 遍历服务顺序
    for (const service of serviceOrder) {
      // 跳过说明行
      if (service.startsWith('An asterisk')) continue;
      

      // 解析服务信息
      const deviceMatch = service.match(/Device: (.+))/);
      if (!deviceMatch) continue;

      const deviceName = deviceMatch[1];
      const serviceMatch = service.match(/(d+) (.+)n/);
      if (!serviceMatch) continue;

      const serviceName = serviceMatch[1];
      
      // 跳过串口
      if (serviceName.includes('Serial Port')) continue;

      // 检查设备是否在已连接列表中
      const isConnected = connectedPorts.some(port => 
        port.includes(`Device: ${deviceName}`)
      );

      if (!isConnected) continue;

      try {
        // 获取代理信息
        const httpProxy = execSync(`networksetup -getwebproxy "${serviceName}"`).toString();
        const httpsProxy = execSync(`networksetup -getsecurewebproxy "${serviceName}"`).toString();
        
        const proxyConfig = {
          service: serviceName,
          device: deviceName,
          httpProxy: parseProxyConfig(httpProxy),
          httpsProxy: parseProxyConfig(httpsProxy)
        };

        activeServices.push(proxyConfig);
      } catch (err) {
        console.error(`Error getting proxy for service ${serviceName}:`, err.message);
        continue;
      }
    }

    return activeServices;
  } catch (err) {
    console.error('Error getting network services:', err);
    return [];
  }
}

function parseProxyConfig(configStr{
  const lines = configStr.split('n');
  return {
    enabled: lines[0].includes('Yes'),
    server: lines[1].split(': ')[1],
    portparseInt(lines[2].split(': ')[1]),
    authenticated: lines[3].includes('Yes')
  };
}

// 使用示例
console.log(getMacNetworkServices());
Axios 的代理问题探究

可以获取到目前可用的服务的系统代理配置,之后按照自定义代理的方式获取IP信息

2) Linux

还是以 Ubuntu Desktop 24.04 为例

未设置系统代理时

Axios 的代理问题探究
Axios 的代理问题探究

设置系统代理后

Axios 的代理问题探究
Axios 的代理问题探究

可以看到,在 Ubuntu Desktop 24.04 中,设置系统代理后,会自动设置环境变量代理,只有在我们使用环境变量代理时(即默认配置)才有效

接下来测试一下 App 的情况

未开启系统代理时

Axios 的代理问题探究

开启系统代理后

Axios 的代理问题探究

可以看到,配置并开启系统代理后,会自动配置环境变量代理

Axios 的代理问题探究
Axios 的代理问题探究

比较奇怪的是,之前设置了环境变量代理后,axios 默认配置是可以使用环境变量代理的,但是在配置系统代理后,系统代理自动配置环境变量代理时,竟然不能用了

于是,又经过了一系列重启、重新测试环境变量代理

Axios 的代理问题探究

结果还是一样,我们再换 Deepin 23 进行测试

未开启系统代理时

Axios 的代理问题探究

开启系统代理后

Axios 的代理问题探究
Axios 的代理问题探究
Axios 的代理问题探究

系统代理情况与 Ubuntu Desktop 24.04 一致,我们测试一下直接设置环境变量代理

Axios 的代理问题探究
Axios 的代理问题探究

测试结果和 Ubuntu Desktop 24.04 一致

对于这个结果,我们肯定是感觉比较奇怪的,为什么会这样呢?

看来系统代理自动配置环境变量代理的方式和我们配置环境变量代理的方式不一样,之前我们为了保证配置环境变量对所有程序生效,是直接将配置信息写在了 /etc/profile 或者 /etc/environment 中的,如果我们把配置写入到默认shell的个人用户配置文件呢?例如 fish 的个人配置文件位置为 ~/.config/fish/config.fish

Axios 的代理问题探究
Axios 的代理问题探究

可以看到,我们通过 shell 默认的个人配置文件配置环境变量代理时是不起作用的,所以环境变量代理还挺复杂的

综上,在 Linux 中,系统代理会自动配置环境变量代理,终端执行的脚本中的 axios 默认配置会自动使用环境变量代理,而直接点击启动的 App 中的 axios 则不会使用系统代理

3) Windows

Axios 的代理问题探究
Axios 的代理问题探究

在 Windows 中设置环境变量后,并不会自动设置环境变量代理

此时测试脚本中  axios 默认配置以及 { proxy: false } 配置下代理使用情况

Axios 的代理问题探究
Axios 的代理问题探究

可以看到,默认情况下,脚本中的 axios 是不会使用系统代理的,我们看一下 App

Axios 的代理问题探究
Axios 的代理问题探究

可以看到,默认情况下,直接通过图标点击进入的 Electron App 中的 axios 同样不使用系统代理,此时例如 edge 浏览器是默认使用系统代理的

0x04 环境变量代理的进一步测试

更新关于环境变量的结论,通过 /etc/profile/etc/environment 配置的代理对 App 中的 axios 默认配置有效,通过 shell 的默认配置文件 .bashrc 等配置的环境变量代理对 App 中的 axios 默认配置无效

这是为什么呢?猜测是 electron 只能获取到系统环境变量文件中的配置

系统级环境变量文件

/etc/environment       # 系统级环境变量,所有用户生效
/etc/profile # 全局配置文件
/etc/profile.d/*.sh # profile 的子配置文件

用户级环境变量文件

~/.profile            # 用户级环境变量
~/.bashrc # bash 配置文件
~/.bash_profile # bash 登录配置
~/.bash_login # bash 登录配置的替代文件
~/.zshrc # zsh 配置文件
~/.config/fish/config.fish # fish 配置文件

为此,我们使用 Electron-vite 开发一个程序来测试,主要代码如下

ipcMain.handle('get-proxy', () => {
    const httpProxy = process.env.http_proxy || process.env.HTTP_PROXY;
    const httpsProxy = process.env.https_proxy || process.env.HTTPS_PROXY;
    const noProxy = process.env.no_proxy || process.env.NO_PROXY;

    return {
      http: httpProxy,
      https: httpsProxy,
      no_proxy: noProxy
    };
  })
Axios 的代理问题探究

点击按钮后,就显示出代理信息,使用 npm run build:linux 编译成 deb ,并安装

首先测试用户级环境变量配置文件

export http_proxy=http://192.168.1.127:1080
export https_proxy=http://192.168.1.127:1080
export no_proxy=localhost.127.0.0.0/8,::1
Axios 的代理问题探究
Axios 的代理问题探究

可以看到,我们虽然已经在用户级环境变量文件中都配置了代理信息,但 Electron 打包后的程序完全没有获取到,也就是 process.env.xxx 没有获取到

接下来测试系统级环境变量

/etc/environment

Axios 的代理问题探究
Axios 的代理问题探究

成功获取到环境变量代理信息

/etc/profile

Axios 的代理问题探究
Axios 的代理问题探究

由于 Deepin 23 上似乎并不使用这个配置文件,所以采用 Ubuntu 来进行测试,结果如上

/etc/profile.d/*.sh

Axios 的代理问题探究
Axios 的代理问题探究

成功获取到环境变量代理信息

0x05 总结

通过有限的测试,最终得到如下结论,结论均为 axios 的行为,所以简略写

Axios 的代理问题探究

0x06

原文始发于微信公众号(NOP Team):Axios 的代理问题探究

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年1月11日12:39:39
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Axios 的代理问题探究https://cn-sec.com/archives/3603946.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息