Insecure deserialization In axios
Description
Axios: Fetch Adapter Header Injection via Inherited FormData getHeaders
Summary
Axios contains a guard in the Node HTTP adapter to avoid using an inherited Object.prototype.getHeaders as a FormData header source. The fetch adapter calls the shared resolveConfig() helper before dispatch, and that helper lacks the same guard. If another vulnerability pollutes Object.prototype with FormData-like properties and getHeaders(), the fetch adapter can merge attacker-controlled headers into the outbound request.
Axios does not create the prototype pollution source. This is a read-side gadget in the fetch adapter configuration path.
Impact
An attacker with a prior same-process prototype-pollution primitive can inject headers into fetch-adapter requests. Depending on the target service, this may affect authorization, metadata-service access, cache behavior, conditional request handling, or other application-specific header logic.
Plain objects are blocked by current FormData detection. The confirmed path uses arrays or non-plain class instances whose prototype chain can resolve polluted FormData-like properties.
Affected Functionality
Affected:
Fetch adapter requests.
resolveConfig() handling of utils.isFormData(data).
Request bodies that can be spoofed as FormData through inherited Symbol.toStringTag, append, and getHeaders.
Not affected:
Node HTTP adapter's later FormData header path, which checks data.getHeaders !== Object.prototype.getHeaders.
Plain object request bodies rejected by current isFormData() plain-object guard.
Processes without prototype pollution.
Technical Details
lib/helpers/resolveConfig.js currently contains:
if (utils.isFormData(data)) { if (platform.hasStandardBrowserEnv || platform.hasStandardBrowserWebWorkerEnv || utils.isReactNative(data)) { headers.setContentType(undefined); } else if (utils.isFunction(data.getHeaders)) { setFormDataHeaders(headers, data.getHeaders(), own('formDataHeaderPolicy')); } }
Unlike lib/adapters/http.js, this code does not reject Object.prototype.getHeaders. Local verification on axios 1.18.1 polluted Object.prototype[Symbol.toStringTag], append, and getHeaders, then sent an array body with adapter: 'fetch'. The loopback server received X-Poisoned: yes.
Proof of Concept of Attack
Constrained local demonstration:
Object.prototype[Symbol.toStringTag] = 'FormData'; Object.prototype.append = function () {}; Object.prototype.getHeaders = () => ({ 'X-Poisoned': 'yes' }); await axios.post(url, ['a', 'b'], { adapter: 'fetch' });
Expected safe behavior is that inherited Object.prototype.getHeaders is ignored. Current affected behavior merges the returned header.
Workarounds
Use the Node HTTP adapter for server-side requests that may run in a polluted process. Avoid passing array or class-instance bodies through the fetch adapter when prototype pollution is suspected.
Original report
Summary
The Node HTTP adapter contains a guard that prevents Object.prototype.getHeaders from being used as a FormData header source. The shared resolveConfig() helper does not have the same guard. The fetch adapter calls resolveConfig(), so it can still merge headers returned by inherited data.getHeaders().
This is a patch mismatch for the FormData prototype-pollution header-injection class.
Affected Version
Validated on:
axios: 1.17.0
commit: 4306df2
runtime: Node.js v24.15.0
Preconditions
Application uses adapter: 'fetch'.
A separate prototype-pollution primitive can write:
Object.prototype[Symbol.toStringTag] = 'FormData'
Object.prototype.append = function () {}
Object.prototype.getHeaders = function () { ... }
The request body is an array or custom class instance. Plain objects are blocked by the current isFormData() plain-object guard.
Root Cause
lib/adapters/http.js contains:
data.getHeaders !== Object.prototype.getHeaders
But lib/helpers/resolveConfig.js only checks:
} else if (utils.isFunction(data.getHeaders)) { setFormDataHeaders(headers, data.getHeaders(), own('formDataHeaderPolicy')); }
The fetch adapter calls resolveConfig(config) before dispatching the request.
Impact
An attacker can inject arbitrary headers into fetch-adapter requests. This may be used to influence internal APIs, metadata services, cache behavior, or application-specific authorization checks.
Proof of Concept
import axios from './index.js'; import http from 'http'; const start = (handler) => new Promise((resolve) => { const server = http.createServer((req, res) => { let body = ''; req.on('data', (chunk) => (body += chunk)); req.on('end', () => handler(req, res, body));...
Observed wire request:
{ "headers": { "x-poisoned": "yes", "content-type": "text/plain;charset=UTF-8", "content-length": "3" }, "body": "a,b" }...
References
https://github.com/axios/axios/security/advisories/GHSA-6chq-wfr3-2hj9
https://osv.dev/vulnerability/GHSA-6chq-wfr3-2hj9
Related patch area: lib/adapters/http.js, lib/helpers/resolveConfig.js
Mitigation
Update Impact
Minimal update. May introduce new vulnerabilities or breaking changes.
Ecosystem | Component | Affected version | Patched versions |
|---|---|---|---|
debian 13 | - | ||
debian 14 | 1.20.0-1 | ||
npm | 1.20.0 |
Aliases
References