GitHub Advisoryadvisory · npm
GHSA-xj6q-8x83-jv6g Axios: Prototype pollution auth subfields can inject Basic auth
MEDIUM
Official page ↗
- Aliases
- CVE-2026-67314
- Ecosystem
- npm
- Packages
- axios
- Severity
- Moderate
- CVSS vector
- CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:L/SC:N/SI:N/SA:N
- Weaknesses
- CWE-1321
- Reviewed
- 2026-07-20
Affected packages
• npm axios: >= 1.15.2, < 1.18.0 → fixed in 1.18.0
Description
Summary
Axios versions after the `GHSA-q8qp-cvcw-x6jj` fix still contain prototype-pollution read-side gadgets in Basic auth subfield handling. If a host application is already affected by prototype pollution and then makes an axios request with an own `auth` object that omits `username` or `password`, axios reads inherited `Object.prototype.username` and `Object.prototype.password` values and uses them to construct an outbound `Authorization: Basic ...` header.
This does not mean axios itself pollutes prototypes. Exploitation requires a separate prototype-pollution primitive in the host process, plus an axios call pattern such as `auth: opts.auth || {}`.
Impact
An attacker who can pollute `Object.prototype.username` and/or `Object.prototype.password` can influence the Basic auth header on affected axios requests that pass an empty or partial own `auth` object.
The practical impact is outbound request tampering. The attacker can inject attacker-chosen Basic auth credentials, replace an existing `Authorization` header because axios removes it when `auth` is used, or cause downstream authorization failures.
This should not be described as automatic credential exfiltration. In the minimal reproduced case, the Basic auth values are attacker-controlled values, not secrets read from axios. Credential disclosure requires an additional application-specific condition, such as a request destination observable by the attacker and a partial real auth object with a missing polluted subfield.
Affected Functionality
Affected functionality:
• Node HTTP adapter Basic auth handling in `lib/adapters/http.js`.
• Browser, web worker, React Native, and fetch shared resolver Basic auth handling in `lib/helpers/resolveConfig.js`.
• Requests where `config.auth` is an own object but `username` and/or `password` are absent own properties.
Unaffected or not accepted as core impact:
• Requests with no own `auth` object after `mergeConfig()`.
• Requests with own `auth.username` and `auth.password` values.
• Normal axios request flow for inherited top-level `params` / `paramsSerializer` after the null-prototype `mergeConfig()` hardening.
• Attacker-controlled `paramsSerializer` functions from JSON-only prototype pollution, because JSON pollution cannot create functions. If attacker-controlled code can install functions in the process, that is outside axios’ runtime boundary.
Technical Details
`mergeConfig()` returns a null-prototype top-level config object, which prevents top-level reads such as `config.auth` from inheriting polluted values. However, nested plain objects returned by `utils.merge()` still have `Object.prototype`.
In `lib/adapters/http.js`, axios correctly reads the top-level `auth` value through `own('auth')`, but then reads subfields directly:
```js
const configAuth = own('auth');
if (configAuth) {
const username = configAuth.username || '';
const password = configAuth.password || '';
auth = username + ':' + password;
}
```
If the caller passes auth: {} and Object.prototype.username/password are polluted, those direct subfield reads walk the prototype chain.
The same pattern exists in `lib/helpers/resolveConfig.js`:
```js
if (auth) {
headers.set(
'Authorization',
'Basic ' +
btoa((auth.username || '') + ':' + (auth.password ? encodeUTF8(auth.password) : ''))
);
}
```
The fix should guard `username` and `password` with `utils.hasOwnProp`, matching the proxy-auth pattern already used elsewhere.
Proof of Concept of Attack
Safe local PoC against published `[email protected]`:
```js
const http = require('node:http');
const axios = require('axios');
Object.prototype.username = 'victim-user';
Object.prototype.password = 'victim-password-leaked';
const server = http.createServer((req, res) => {
console.log({
url: req.url,
authorization: req.headers.authorization || null
});
res.end('{}');
server.close(() => {
delete Object.prototype.username;
delete Object.prototype.password;
});
});
server.listen(0, '127.0.0.1', async () => {
await axios.get(`http://127.0.0.1:${server.address().port}/api`, {
auth: {}
});
});
```
Expected output:
```json
{
"url": "/api",
"authorization": "Basic dmljdGltLXVzZXI6dmljdGltLXBhc3N3b3JkLWxlYWtlZA=="
}
```
The base64 value decodes to `victim-user:victim-password-leaked`.
Workarounds
Avoid passing empty or partial `auth` objects. Only set `auth` when the application has own username and password values.
Applications that merge untrusted input should filter `__proto__`, `constructor`, and `prototype`, and should read optional user options with own-property checks rather than `opts.auth || {}`.
Where a wrapper must materialize optional auth, use a null-prototype object or explicitly copy only own fields.
Original Report
Summary
After GHSA-q8qp-cvcw-x6jj / PR #10779 (shipped in `v1.15.2`) and the further proxy-side hardening in
PR #10833 (merged 2026-05-02), the top-level `config.auth` and the proxy authsub-fields are correctly read via `utils.hasOwnProp`. The regular request auth sub-fields (`config.auth.username` and `config.auth.password`) and the `config.params` / `config.paramsSerializer` reads inside `resolveConfig.js` are still unguarded against a polluted `Object.prototype`.
When a polluted host process makes an axios call with the common "optional override" pattern (`auth: opts.auth || {}` — an empty own `{}`), the sub-field reads `configAuth.username` and `configAuth.password` walk the prototype chain and return the attacker-controlled values. Same for `params` and `paramsSerializer`. The outbound HTTP request then carries an attacker-chosen `Authorization: Basic ` header and an attacker-chosen querystring, leaking credentials and exfiltrating data to whichever host the request goes to (often attacker-influenced too — i.e. the amplifier is wired into many credential-stuffing chains).
Reproduces against `axios` `main` HEAD (`34723be`, dated 2026-05-24)
as well as the released `v1.16.1`.
Details
Three still-unguarded read sites on `main` HEAD:
(1) `lib/adapters/http.js` lines 737–740 (Node http adapter):
```js
const configAuth = own('auth'); // ← top-level guard OK
if (configAuth) {
const username = configAuth.username || ''; // ← reads .username on the inherited chain
const password = configAuth.password || ''; // ← reads .password on the inherited chain
auth = username + ':' + password;
}
```
`own('auth')` correctly applies `hasOwnProp` to the top-level `auth`
key. But once `configAuth` is the empty object the caller passed
(`auth: {}`), `configAuth.username` walks the prototype chain and
picks up `Object.prototype.username`.
Contrast with the proxy-auth path that PR #10833 fixed (lines 322–324):
```js
const authUsername =
authIsObject && utils.hasOwnProp(proxyAuth, 'username') ? proxyAuth.username : undefined;
const authPassword =
authIsObject && utils.hasOwnProp(proxyAuth, 'password') ? proxyAuth.password : undefined;
```
This is the exact pattern needed at lines 739–740 too.
(2) `lib/helpers/resolveConfig.js` lines 50 + 68 (xhr/fetch adapter shared resolver):
```js
const auth = own('auth'); // ← top-level guard OK
...
btoa((auth.username || '') + ':' + (auth.password ? encodeUTF8(auth.password) : ''))
// ^ .username and .password read directly on `auth`, no hasOwnProp guard
```
Same shape — top-level guarded, sub-fields walk prototype.
(3) `lib/helpers/resolveConfig.js` lines 58–59 (params + paramsSerializer):
```js
newConfig.url = buildURL(
buildFullPath(baseURL, url, allowAbsoluteUrls),
config.params, // ← direct read, not through own()
config.paramsSerializer // ← direct read, not through own()
);
```
This third site is already…
Related
Alias · CVE (1)
- CVE-2026-67314 axios versions >=1.15.2 and <1.18.0 contain prototype-pollution read-side gadgets in Basic auth subfield handling (lib/adapters/http.js and… vulnerability
Weakness · CWE (1)
- CWE-1321 Improperly Controlled Modification of Object Prototype Attributes weakness
References
- https://github.com/axios/axios/security/advisories/GHSA-xj6q-8x83-jv6g web
- https://nvd.nist.gov/vuln/detail/CVE-2026-67314 advisory
- https://github.com/axios/axios/pull/11000 web
- https://github.com/axios/axios/commit/32fc489632377d214db55bfa4e2c48486a7d7ce2 web
- https://github.com/axios/axios package
- https://github.com/axios/axios/releases/tag/v1.18.0 web
- https://www.vulncheck.com/advisories/axios-before-prototype-pollution-via-auth-subfields web