Skip to content

关于 2025 年发现的 axios 低版本存在高危漏洞 CVE-2025-58754 问题

axios 低版本存在高危漏洞,CVE-2025-58754

漏洞描述

https://nvd.nist.gov/vuln/detail/CVE-2025-58754

这个漏洞只在 axios 在 node.js 服务端运行的时候有问题,浏览器上没说有问题。这个漏洞在 0.30.2 和 1.12.0 版本已经修复。

原文描述:

Axios 是一个基于承诺的 HTTP 客户端,适用于浏览器和 Node.js。当 Axios 在 0.30.2 和 1.12.0 版本之前运行于 Node.js 上,并获得带有“data:” 方案的 URL 时,它不会执行 HTTP。相反,其节点 http 适配器将整个有效载荷解码到内存(“缓冲区”/“Blob”)中,并返回合成的 200 响应。该路径忽略了仅保护 HTTP 响应的 'maxContentLength' / 'maxBodyLength',因此攻击者可以提供非常庞大的 'data:' URI,导致进程分配无界内存并崩溃(DoS),即使调用者请求 'responseType: 'stream'' 也无法实现。0.30.2 和 1.12.0 版本包含该问题的补丁。

补丁链接:

1.x 的补丁提交记录:https://github.com/axios/axios/commit/945435fc51467303768202250debb8d4ae892593

0.x 的补丁提交记录:https://github.com/axios/axios/commit/a1b1d3f073a988601583a604f5f9f5d05a3d0b67

漏洞是如何修复的

查看上文中 1.x 的补丁提交记录

核心修改的文件:

  • lib/adapters/http.js 修改 http 适配器逻辑
  • lib/helpers/estimateDataURLDecodedBytes.js 新增工具函数
  • test/unit/helpers/estimateDataURLDecodedBytes.spec.js 新增对工具函数 estimateDataURLDecodedBytes 的单元测试

修改适配器逻辑

为什么说这个只是在NodeJS环境上修改了?

查看修改记录:

js
// https://github.com/axios/axios/commit/945435fc51467303768202250debb8d4ae892593#diff-586c04c24608b86a074073c5514cc195be5c4c87ddb56da233f6d998ada4159fR172
if (config.maxContentLength > -1) {
  // Use the exact string passed to fromDataURI (config.url); fall back to fullPath if needed.
  const dataUrl = String(config.url || fullPath || '');
  const estimated = estimateDataURLDecodedBytes(dataUrl);

  if (estimated > config.maxContentLength) {//PS: 我看了一下源码 config.maxContentLength 默认值是 2000,默认只对 node 环境生效
    return reject(new AxiosError(
      'maxContentLength size of ' + config.maxContentLength + ' exceeded',
      AxiosError.ERR_BAD_RESPONSE,
      config
    ));
  }
}

所在的函数为

js
//https://github.com/axios/axios/commit/945435fc51467303768202250debb8d4ae892593#diff-586c04c24608b86a074073c5514cc195be5c4c87ddb56da233f6d998ada4159fR172
export default isHttpAdapterSupported && function httpAdapter(config) {
  // ...
}

而 isHttpAdapterSupported 函数在:

js
// https://github.com/axios/axios/commit/945435fc51467303768202250debb8d4ae892593#diff-586c04c24608b86a074073c5514cc195be5c4c87ddb56da233f6d998ada4159fR130
const isHttpAdapterSupported = typeof process !== 'undefined' && utils.kindOf(process) === 'process';

process 只在 node.js 环境中才有定义,而浏览器中是不存在的,所以这个 commit 是针对 node.js 环境修复的

estimateDataURLDecodedBytes

重点注释一下这个函数:

js
/**
 * Estimate decoded byte length of a data:// URL *without* allocating large buffers.
 * - For base64: compute exact decoded size using length and padding;
 *               handle %XX at the character-count level (no string allocation).
 * - For non-base64: use UTF-8 byteLength of the encoded body as a safe upper bound.
 *
 * @param {string} url
 * @returns {number}
 */
export default function estimateDataURLDecodedBytes(url) {
  if (!url || typeof url !== 'string') return 0; // 空字符串和非字符串直接返回0
  if (!url.startsWith('data:')) return 0; // 不是data url直接返回0

  const comma = url.indexOf(',');// 找到第一个逗号
  if (comma < 0) return 0; // 没有逗号直接返回0

  const meta = url.slice(5, comma);// 从data:开始到第一个逗号是媒体元数据
  const body = url.slice(comma + 1);// 从逗号之后的内容开始是数据
  const isBase64 = /;base64/i.test(meta);// 判断是否是base64编码

  // 如果是base64编码,则计算解码后的字节数(由于base64编码的时候会每三个字节对应四个字符,长度跟实际的字节数有差异,所以需要计算)
  if (isBase64) {
    /**
     * 精确估算 Base64 数据解码后的字节数(bytes):
     * 对于一个合法的 Base64 字符串:
     * 每 4 个字符 → 解码为 3 个字节
     * 如果末尾有 =,表示填充(padding),每有一个 = 就少 1 个输出字节(最多 2 个)
     * 所以:
     * 解码字节数 = ⌊有效字符数 / 4⌋ × 3 − 填充字符数
     * 但问题在于:Data URL 中的 Base64 可能包含 URL 编码,例如:
     * 
     * + 被编码为 %2B
     * / 被编码为 %2F
     * = 被编码为 %3D
     * 这些 %XX 序列在原始字符串中占 3 个字符,但实际只代表 1 个 Base64 字符。
     * 
     * 所以直接用 body.length 会高估长度。
     */
    // --- 第一步:计算“有效 Base64 字符数” effectiveLen ----
    let effectiveLen = body.length;
    const len = body.length; // cache length

    for (let i = 0; i < len; i++) {
      if (body.charCodeAt(i) === 37 /* '%' */ && i + 2 < len) {
        // 处理百分比编码
        const a = body.charCodeAt(i + 1);
        const b = body.charCodeAt(i + 2);
        const isHex =
          ((a >= 48 && a <= 57) || (a >= 65 && a <= 70) || (a >= 97 && a <= 102)) &&
          ((b >= 48 && b <= 57) || (b >= 65 && b <= 70) || (b >= 97 && b <= 102));
        
        if (isHex) {
          effectiveLen -= 2;// %XX 本应算作 1 个字符,但现在占了 3 个,所以减 2
          i += 2;// 跳过 XX,避免重复处理
        }
      }
    }
    // ------------------------------

    // ------第二步:检测填充字符"="数量(pad)-----
    //= 可能被写成 %3D 或 %3d(大小写不敏感)
    let pad = 0;
    let idx = len - 1;

    // 判断位置 j 是否是以 "%3D" 或 "%3d" 结尾
    const tailIsPct3D = (j) =>
      j >= 2 &&
      body.charCodeAt(j - 2) === 37 && // '%'
      body.charCodeAt(j - 1) === 51 && // '3'
      (body.charCodeAt(j) === 68 || body.charCodeAt(j) === 100); // 'D' or 'd'

    if (idx >= 0) {
      if (body.charCodeAt(idx) === 61 /* '=' */) {
        pad++;
        idx--;// 向前移动
      } else if (tailIsPct3D(idx)) {
        pad++;
        idx -= 3;// 跳过整个 "%3D"
      }
    }
    // 如果已经找到 1 个 padding,再检查倒数第二个(最多只检查两个 padding 字符,符合 Base64 规范)
    if (pad === 1 && idx >= 0) {
      if (body.charCodeAt(idx) === 61 /* '=' */) {
        pad++;
      } else if (tailIsPct3D(idx)) {
        pad++;
      }
    }
    // ------------------

    // ---第三步:计算最终字节数-----
    const groups = Math.floor(effectiveLen / 4);// 完整的 4 字符组数
    const bytes = groups * 3 - (pad || 0);// 每组 3 字节,减去填充
    return bytes > 0 ? bytes : 0;
  }

  // 非base64编码,直接使用Node.js 内置的 buffer 计算长度
  return Buffer.byteLength(body, 'utf8');
}

为什么浏览器环境不需要修复,而 Node.js 环境需要修复?

由上文可知,这个 commit 所修复的场景,只是针对 Node.JS 通过 http 获取 data:URL 超过 maxContentLength 大数据量的场景,主要修改的位置在lib/adapters/http.js,而浏览器环境是使用 XMLHttpRequest 或 fetch,代码是在lib/adapters/xhr.jslib/adapters/fetch.js

我们自己去看一下这两个源码部分,发现并没有相关 data:URL 和大数据量的处理,那么为什么?

其实是因为:

  • Node.js 因直接掌控 HTTP 协议栈,需要显式进行资源管理,服务器的资源有限,当然要为防止内存被撑爆做好安全防护。

  • 浏览器环境,有其内置的机制和安全模型,内部自动处理了,不需要我们自己处理。况且浏览器环境本身就支持大数据量的请求,平时这种动不动几 MB 的数据请求,也不是很少见,特别是图片,第三方库等资源的请求。

总结

本文详细介绍了 Axios 低版本存在的高危漏洞的原因,详细介绍了补丁修复范围,以及方法和原理。

Released under the ISC License.