关于 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环境上修改了?
查看修改记录:
// 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
));
}
}所在的函数为
//https://github.com/axios/axios/commit/945435fc51467303768202250debb8d4ae892593#diff-586c04c24608b86a074073c5514cc195be5c4c87ddb56da233f6d998ada4159fR172
export default isHttpAdapterSupported && function httpAdapter(config) {
// ...
}而 isHttpAdapterSupported 函数在:
// 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
重点注释一下这个函数:
/**
* 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.js和lib/adapters/fetch.js。
我们自己去看一下这两个源码部分,发现并没有相关 data:URL 和大数据量的处理,那么为什么?
其实是因为:
Node.js 因直接掌控 HTTP 协议栈,需要显式进行资源管理,服务器的资源有限,当然要为防止内存被撑爆做好安全防护。
浏览器环境,有其内置的机制和安全模型,内部自动处理了,不需要我们自己处理。况且浏览器环境本身就支持大数据量的请求,平时这种动不动几 MB 的数据请求,也不是很少见,特别是图片,第三方库等资源的请求。
总结
本文详细介绍了 Axios 低版本存在的高危漏洞的原因,详细介绍了补丁修复范围,以及方法和原理。