CVE-2026-41487 Overview
CVE-2026-41487 is a broken access control vulnerability [CWE-284] in Langfuse, an open source large language model (LLM) engineering platform. The flaw affects versions from 3.68.0 to before 3.167.0 in the LLM connection update flow. An authenticated user holding the low-privileged member role within a project can update an existing LLM connection and change its baseUrl to an attacker-controlled endpoint. Langfuse then reuses the stored provider secret and redirects the test request to the attacker, exposing the plaintext provider LLM API key.
Critical Impact
A project member can exfiltrate plaintext provider LLM API keys (for example OpenAI or Anthropic keys) by redirecting test traffic to an attacker-controlled URL.
Affected Products
- Langfuse 3.68.0 through versions prior to 3.167.0
- Self-hosted Langfuse deployments with multi-user project membership
- Langfuse instances using stored LLM provider API keys
Discovery Timeline
- 2026-05-08 - CVE-2026-41487 published to NVD
- 2026-05-13 - Last updated in NVD database
Technical Details for CVE-2026-41487
Vulnerability Analysis
The vulnerability resides in the Langfuse tRPC router that handles LLM API key management, specifically in web/src/features/llm-api-key/server/router.ts. The update flow accepted partial input where secretKey could be omitted while baseURL was changed. When the client omitted secretKey, the server decrypted and reused the stored provider secret. The test endpoint then issued an outbound request to the new baseURL using that decrypted secret in the authentication header.
A second defect compounded the issue: the test mutation on the LLM connection router did not enforce the llmApiKeys:create scope. Any authenticated project member could invoke connection tests, not only users with write permissions on LLM credentials.
Root Cause
The root cause is missing authorization and missing input coupling validation. The update procedure trusted that baseURL and secretKey were independent fields. It did not require re-supplying the secret when the destination URL changed. Combined with the absent scope check on the test mutation, a member role user could trigger a server-side request that leaked the decrypted credential to any URL of their choosing.
Attack Vector
The attacker must already be an authenticated member of a Langfuse project. The attacker hosts an HTTP listener that captures incoming Authorization headers. They then call the LLM connection update or test endpoint with baseURL set to their listener and secretKey left blank or null. Langfuse decrypts the stored API key, sends a test completion request to the attacker host, and the attacker harvests the bearer token from the captured request.
// Security patch - require secret key when base URL changes
// Source: https://github.com/langfuse/langfuse/commit/7527bb0d84bc0a3dc24a4b16d22ed2e46e6dddff
const hasNewSecretKey =
typeof input.secretKey === "string" && input.secretKey.length > 0;
const baseURL = input.baseURL ?? existingKey.baseURL;
const isBaseURLChanged = baseURL !== existingKey.baseURL;
if (isBaseURLChanged && !hasNewSecretKey) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Secret key is required when changing the base URL",
});
}
const secretKey = hasNewSecretKey
? (input.secretKey as string)
: decrypt(existingKey.secretKey);
The companion patch enforces the missing authorization scope on the test endpoint:
// Security patch - enforce write scope on the test mutation
// Source: https://github.com/langfuse/langfuse/commit/e12386f9d4368bbfff24a4ad7fd53641091605ff
test: protectedProjectProcedureWithoutTracing
.input(CreateLlmApiKey)
.mutation(async ({ input, ctx }) => {
throwIfNoProjectAccess({
session: ctx.session,
projectId: input.projectId,
scope: "llmApiKeys:create",
});
return testLLMConnection({
adapter: input.adapter,
provider: input.provider,
Detection Methods for CVE-2026-41487
Indicators of Compromise
- Outbound HTTP requests from Langfuse server to unexpected hosts, particularly those not matching known LLM provider domains such as api.openai.com or api.anthropic.com.
- Audit log entries showing LLM API key update or test events triggered by users with the member role.
- Unusual baseURL values in the llm_api_keys table pointing to attacker-controlled or recently registered domains.
Detection Strategies
- Review Langfuse application logs for calls to the LLM API key update and test tRPC procedures correlated with user role context.
- Inspect egress network telemetry for HTTP requests originating from Langfuse pods or hosts that carry provider Authorization: Bearer headers to non-provider destinations.
- Compare historical baseURL values against current values in the LLM connection store to identify unauthorized changes.
Monitoring Recommendations
- Enable verbose audit logging on the Langfuse project membership and credential management routes.
- Implement egress filtering or DNS allow-listing for the Langfuse worker so it can only reach approved LLM provider endpoints.
- Alert on any change to LLM connection records where the actor is not a project owner or admin.
How to Mitigate CVE-2026-41487
Immediate Actions Required
- Upgrade all Langfuse deployments to version 3.167.0 or later without delay.
- Rotate every stored LLM provider API key configured in affected Langfuse instances, since exposure cannot be ruled out.
- Audit project membership and remove member accounts that should not have access to projects holding sensitive provider credentials.
Patch Information
The issue is fixed in Langfuse v3.167.0. The fix is delivered through two commits: commit 7527bb0 requires a new secret key when the base URL changes, and commit e12386f enforces the llmApiKeys:create scope on the test endpoint. Full details are in GHSA-2524-j966-gfgh.
Workarounds
- Restrict project membership to trusted users only until the patch is applied.
- Apply network egress controls on the Langfuse server so outbound calls are limited to known LLM provider domains.
- Temporarily remove stored LLM API keys from Langfuse and require per-request key supply where feasible.
# Upgrade Langfuse via Docker to the patched version
docker pull langfuse/langfuse:3.167.0
docker stop langfuse-web
docker rm langfuse-web
docker run -d --name langfuse-web \
--env-file .env \
-p 3000:3000 \
langfuse/langfuse:3.167.0
# Verify the running version
curl -s http://localhost:3000/api/public/health | jq .version
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

