Back to Blog
critical SEVERITY6 min read

Yandex Translate API Key Leaked via URL Query Parameter

The `translateYandex()` helper built its request URL by interpolating the caller-supplied API key directly into the query string, meaning every call leaked the credential into server access logs, proxy logs, and any Referer header sent by intermediaries. The fix switches the request from a GET with the key in the URL to a POST with the key in the request body via `URLSearchParams`, removing the credential from any URL-logging surface entirely.

O
By Orbis AppSec
•Published September 25, 2026•Reviewed September 25, 2026

TITLE: Yandex Translate API Key Leaked via URL Query Parameter

SEO_TITLE: Yandex API Key in URL: CWE-798 POST Fix

SEO_DESCRIPTION: translateYandex() sent the Yandex Translate API key as a URL query string, leaking it via logs and Referer headers; CWE-798 fixed by moving it to a POST body.

SUMMARY: The translateYandex() helper built its request URL by interpolating the caller-supplied API key directly into the query string, meaning every call leaked the credential into server access logs, proxy logs, and any Referer header sent by intermediaries. The fix switches the request from a GET with the key in the URL to a POST with the key in the request body via URLSearchParams, removing the credential from any URL-logging surface entirely.

INTRODUCTION: The translateYandex(text, targetLang, apiKey) function in the translation helper module built its outbound request like this: https://translate.yandex.net/api/v1.5/tr.json/translate?key=${apiKey}&text=...&lang=en-${targetLang}, then fired it with a plain axios({url, timeout: 100000}) GET call. Nothing here hardcodes a secret in source — the key is passed in as a parameter — but the transmission mechanism itself is the vulnerability. Any value placed in a URL query string is liable to be written to web server access logs, forwarding proxy logs, browser history (if the URL is ever rendered client-side), and the Referer header of any subsequent request triggered from a page displaying that URL. For an application that calls a third-party translation API on behalf of many requests, this means the API key shows up, in plaintext, in every log line that records an outbound HTTP request URL — a durable, easily overlooked leak.

Affected Versions

Affected not applicable (first-party code)
Fixed in not applicable (first-party code)
Ecosystem npm
CVE / GHSA not assigned
CWE CWE-798 (Use of Hard-coded Credentials)

The Vulnerability Explained

Here is the vulnerable request construction:

const url = `https://translate.yandex.net/api/v1.5/tr.json/translate?key=${apiKey}&text=${encodeURIComponent(text)}&lang=en-${targetLang}`;
const response = await axios({url, timeout: 100000});

The problem is the key=${apiKey} segment. Because apiKey is embedded directly in the URL rather than sent in a request body or header, it travels through every layer that touches the URL string:

  • Access logs on any reverse proxy, load balancer, or CDN sitting in front of the outbound call (or logging the outbound call) will record the full URL, key included.
  • Referer headers: if this URL were ever surfaced in a browser context or forwarded by an intermediate service, the Referer header on the next hop would carry the key to a third party.
  • Application-level logging: it's common practice to log axios request configs or errors, including the url field, for debugging — which means the key ends up in application logs by default, not by mistake.
  • Browser history / dev tools: if this call is ever proxied through a client-side network layer during development or debugging, the key sits in the network tab and history.

An attacker doesn't need to compromise the translation service to exploit this — they just need read access to any of those log stores, which is a far more common foothold than direct code execution. Once obtained, the Yandex API key can be reused to make translation calls billed to the victim's account, potentially exhausting quota or incurring cost, and depending on the key's scope, could expose usage patterns tied to translated text.

The Fix

The fix moves the API key out of the URL and into a POST request body, using URLSearchParams to encode the parameters:

const url = "https://translate.yandex.net/api/v1.5/tr.json/translate";
const body = new URLSearchParams({key: apiKey, text, lang: `en-${targetLang}`});
const response = await axios.post(url, body, {timeout: 100000});

Two things changed, and both matter:

  1. axios({url}) → axios.post(url, body, ...): switching HTTP methods means the URL that gets logged by any proxy, load balancer, or axios interceptor is now just https://translate.yandex.net/api/v1.5/tr.json/translate — no key, no text, no target language. The sensitive parameter no longer exists in any URL-based log surface.
  2. Query string → URLSearchParams body: the key, text, and language are now form-encoded in the POST body, which is not captured by standard access-log configurations and is not reflected in Referer headers, since those only ever carry the URL of the referring page/request, not its body.

The included regression test drives this home by asserting that whatever URL is actually sent to axios.request/axios.post never contains key=${apiKey} for a range of key values, including one with special characters — confirming the credential genuinely never appears in the URL under adversarial input.

Key Takeaways

  • Passing any credential as a URL query parameter is a leak vector even if the credential itself isn't hardcoded — translateYandex()'s apiKey argument was always caller-supplied, yet the URL construction on line 30 still exposed it.
  • axios({url}) with a GET-style query string logs identically to any other outbound URL; if your logging captures request URLs (a very common default), any secret in that URL is now durably persisted in log storage.
  • Moving from ?key=${apiKey}&text=... to a POST body via URLSearchParams({key: apiKey, text, lang}) removes the secret from every URL-based log, proxy trace, and Referer header in one change.
  • When wrapping a third-party API that requires a key, check whether that API supports the key in a header or POST body before defaulting to whatever query-string example is in its documentation.

How Orbis AppSec Detected This

  • Source: the apiKey parameter passed into translateYandex(text, targetLang, apiKey)
  • Sink: the outbound axios({url}) GET request whose url string embedded key=${apiKey}
  • Missing control: no separation between the credential and the URL — the key was interpolated into a query string transmitted and logged as part of the request URL
  • CWE: CWE-798 (Use of Hard-coded Credentials) — flagged for insecure credential handling in transit
  • Fix: the request was rewritten to send the key in a POST body via URLSearchParams instead of the URL query string

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

This finding is a reminder that credential exposure isn't limited to hardcoded values in source — how a credential is transmitted matters just as much as where it's stored. translateYandex() never hardcoded the Yandex API key, but by placing it in the URL query string of an axios GET request, it guaranteed the key would be written into every log surface that records request URLs. Switching to axios.post() with the key encoded in a URLSearchParams body closes that leak without changing the function's public signature — callers of translateYandex(text, targetLang, apiKey) see no difference, but the key no longer travels anywhere it can be silently captured.

FAQ:
Q: Does the fix to translateYandex() change its function signature or how callers invoke it?
A: No. translateYandex(text, targetLang, apiKey) keeps the same signature; only the internal HTTP call changed from a GET with a query string to a axios.post() call with a URLSearchParams body.

Q: Why does moving the API key from the URL to a POST body via URLSearchParams actually stop the leak?
A: Standard access logs, proxies, and Referer headers capture the request URL, not the request body, so once key moves out of the query string in axios({url}) and into the POST body, it no longer appears in those logging surfaces.

Q: Was the Yandex API key ever hardcoded in the source for translateYandex()?
A: No — apiKey was always passed in as a function parameter; the vulnerability was the insecure query-string transmission (?key=${apiKey}), not a hardcoded default value.

TAGS: hardcoded-secrets, credential-exposure, nodejs, axios, api-security, information-disclosure

Prevention and further reading

Frequently Asked Questions

Does the fix to `translateYandex()` change its function signature or how callers invoke it?

No. `translateYandex(text, targetLang, apiKey)` keeps the same signature; only the internal HTTP call changed from a GET with a query string to a `axios.post()` call with a `URLSearchParams` body.

Why does moving the API key from the URL to a POST body via `URLSearchParams` actually stop the leak?

Standard access logs, proxies, and `Referer` headers capture the request URL, not the request body, so once `key` moves out of the query string in `axios({url})` and into the POST body, it no longer appears in those logging surfaces.

Was the Yandex API key ever hardcoded in the source for `translateYandex()`?

No — `apiKey` was always passed in as a function parameter; the vulnerability was the insecure query-string transmission (`?key=${apiKey}`), not a hardcoded default value.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #562

Related Articles

critical

Google Generative AI Keys Exposed in Client-Side Fetch URLs

A client-side AI model discovery utility was embedding Google Generative AI API keys directly in fetch request URLs, making them visible to any user inspecting network traffic or browser DevTools. The fix moves the key from the URL query parameter to a secure HTTP header, eliminating exposure while maintaining API authentication.

critical

How credential leakage through console logging happens in JavaScript browser extensions and how to fix it

A browser extension's `src/background/credentials.js` printed the full Strava authentication cookie string — including a signed JWT and CloudFront-Signature values — straight into the extension console via `console.debug`. Anyone who could open DevTools on the background page (or any tooling that scraped the console) could copy a live session and impersonate the user. The fix replaces the credential payload in both log statements with `Boolean(credentials)` and strips a realistic-looking JWT out

critical

How Hardcoded Secrets Compromise Authentication in JavaScript and How to Fix It

A critical vulnerability in `Tool/QuantumultX/Rewrite/RRSP.js` exposed hardcoded API authentication credentials—a TOKEN and UMID device identifier—directly in source code. Anyone with repository access could extract these credentials to impersonate the legitimate user and gain full account access to the RRTV API service. The fix replaced hardcoded secrets with empty placeholders, forcing users to manually configure credentials through secure channels.

critical

How API Key Exposure in Request Bodies happens in React and how to fix it

The Chatbot component in gitforme was transmitting Azure OpenAI API keys inside JSON request bodies, causing them to be logged by servers, proxies, and middleware. By moving the apiKey from requestBody.apiKey to an Authorization header, credentials are now protected from persistence in generic request logging infrastructure.

critical

How Hardcoded API Keys in WASM Modules Happen in KAP and How to Fix Them

A critical security vulnerability in `wasm/kap/standard-lib/fhelp-impl.kap` exposed hardcoded Gemini API keys directly in source code distributed to end users via WASM modules. The fix replaces the embedded credential with secure environment variable retrieval, preventing credential extraction through browser developer tools or binary inspection.

critical

chrome.runtime.onMessage: Missing Sender Validation in Extension

A critical authentication flaw in a Chrome extension's message handling allowed arbitrary extensions and malicious web pages to trigger sensitive operations including script injection and tab manipulation. The vulnerability existed because chrome.runtime.onMessage listeners processed requests without verifying sender identity or origin, trusting any message source by default.