critical · 9.1CVE-2026-48714Jun 25, 2026

CVE-2026-48714: i18next-http-middleware Prototype Pollution via Dotted Missing-Key

Rohit Hatagale
AI Security Researcher, SecureLayer7

A bypass in i18next-http-middleware's missing-key guard lets attackers send a crafted translation key like '__proto__.polluted' over HTTP, which downstream backends split and walk directly into Object

Packagei18next-http-middleware
Ecosystemnpm
Affected< 3.9.7
Fixed in3.9.7
CVE-2026-48714: i18next-http-middleware Prototype Pollution via Dotted Missing-Key

The problem

i18next-http-middleware's missingKeyHandler endpoint (POST /locales/add/:lng/:ns) accepts untrusted key names from the request body and checks them against a denylist of literal strings: __proto__, constructor, and prototype. That check, added in 3.9.3, blocked exact matches but not dotted variants like __proto__.polluted.

When i18next-fs-backend (≤ 2.6.5) receives such a key, its writeFile() splits it on the default keySeparator (.) into segments and passes them to the setPath() walker with no segment-level guard. The walker descends through __proto__ and writes the attacker's value directly onto Object.prototype, affecting every subsequent property lookup in the process.

Proof of concept

http
POST /locales/add/en/translation HTTP/1.1
Host: target.example.com
Content-Type: application/json

{"__proto__.polluted": "pwned"}

# Verify pollution (in the server process after the request):
# console.log(({}).polluted); // => 'pwned'

The root cause is a two-stage failure. The middleware guard compared the raw key against a denylist of literal strings, so __proto__.polluted passed through untouched. The downstream backend then split the key on . into ["__proto__", "polluted"] and the setPath() walker (getLastOfPath in lib/utils.js) had no check against unsafe segment names, so it descended through Object.__proto__ and wrote the caller-controlled value there.

The patch in 3.9.7 adds a utils.hasUnsafeKeySegment(key, keySeparator) helper that splits the key using the configured keySeparator before the denylist check, so any segment matching __proto__, constructor, or prototype causes the whole key to be rejected immediately.

The companion fix in i18next-fs-backend 2.6.6 adds the same guard inside getLastOfPath as defence-in-depth.

The fix

Upgrade i18next-http-middleware to 3.9.7 and i18next-fs-backend to 2.6.6. Both fixes are required for full defence-in-depth. If you cannot upgrade immediately: restrict the missingKeyHandler route to authenticated users only, or set saveMissing: false to disable missing-key persistence for untrusted input, or set keySeparator: false in i18next options to prevent segment splitting (this also disables nested translation keys).

Reported by codeswhite.

References: [1][2][3][4]