CVE-2026-49352 Overview
CVE-2026-49352 is a hardcoded credentials vulnerability [CWE-798] in 9Router, an AI router and token-saving application. Affected versions from 0.2.21 through 0.4.43 use the hardcoded fallback JWT secret 9router-default-secret-change-me when the JWT_SECRET environment variable is not set. Attackers can forge valid auth_token cookies and bypass authentication entirely. The vulnerable code resides in src/app/api/auth/login/route.js, src/middleware.js, and src/lib/auth/dashboardSession.js. Version 0.4.44 fixes the issue by generating a random secret when none is configured.
Critical Impact
Unauthenticated remote attackers can forge JSON Web Tokens (JWTs) using the publicly known fallback secret, granting full dashboard access without valid credentials.
Affected Products
- 9Router versions 0.2.21 through 0.4.43
- Deployments where JWT_SECRET environment variable is unset
- Fixed in 9Router version 0.4.44
Discovery Timeline
- 2026-07-15 - CVE-2026-49352 published to NVD
- 2026-07-16 - Last updated in NVD database
Technical Details for CVE-2026-49352
Vulnerability Analysis
9Router uses the jose library to sign and verify JWTs for dashboard authentication. When the application initializes JWT signing logic, it reads the secret from process.env.JWT_SECRET. If that variable is unset, the code falls back to the string literal 9router-default-secret-change-me. This fallback exists in src/app/api/auth/login/route.js, src/middleware.js, and src/lib/auth/dashboardSession.js.
Because the fallback secret ships in the public GitHub repository, any attacker with network access to a misconfigured 9Router deployment can sign arbitrary JWTs that the server accepts as valid. The forged tokens are placed into the auth_token cookie to impersonate authenticated dashboard users.
Root Cause
The root cause is the use of hardcoded credentials as an authentication fallback. The application prioritizes availability (starting successfully without configuration) over failing closed when secret material is missing. Operators who deploy 9Router without setting JWT_SECRET unknowingly run production instances protected by a publicly documented secret.
Attack Vector
An attacker identifies a 9Router instance exposed over the network, then generates an auth_token JWT signed with 9router-default-secret-change-me. Submitting the forged cookie to any authenticated endpoint grants access without a login exchange. No user interaction, prior authentication, or elevated privileges are required.
// Patch in src/lib/auth/dashboardSession.js (v0.4.44)
import { SignJWT, jwtVerify } from "jose";
+import fs from "node:fs";
+import path from "node:path";
+import crypto from "node:crypto";
+import { DATA_DIR } from "@/lib/dataDir";
-const SECRET = new TextEncoder().encode(
- process.env.JWT_SECRET || "9router-default-secret-change-me"
-);
+function loadJwtSecret() {
+ if (process.env.JWT_SECRET) return process.env.JWT_SECRET;
+ const file = path.join(DATA_DIR, "jwt-secret");
+ try {
+ return fs.readFileSync(file, "utf8").trim();
+ } catch {}
+ fs.mkdirSync(DATA_DIR, { recursive: true });
+ const generated = crypto.randomBytes(32).toString("hex");
+ fs.writeFileSync(file, generated, { mode: 0o600 });
+ return generated;
+}
+
+const SECRET = new TextEncoder().encode(loadJwtSecret());
Source: GitHub Commit fe3ce25
Detection Methods for CVE-2026-49352
Indicators of Compromise
- Presence of auth_token cookies signed with the HMAC key 9router-default-secret-change-me
- Successful dashboard sessions without corresponding entries in login route logs
- 9Router processes running with JWT_SECRET unset in the process environment
- Access to sensitive routes such as /api/provider-nodes/validate, /api/cli-tools, /api/mcp, and /api/translator from unexpected source IPs
Detection Strategies
- Decode observed auth_token JWTs and attempt verification with the known fallback secret to identify forged tokens
- Inventory running 9Router instances and check the deployed version against 0.4.44
- Audit reverse proxy logs for authenticated requests that lack a preceding POST /api/auth/login call
Monitoring Recommendations
- Alert on any 9Router deployment running a version earlier than 0.4.44
- Monitor environment variables at deployment time to confirm JWT_SECRET is defined
- Log and review all dashboard access events, correlating source IP addresses with expected administrative ranges
How to Mitigate CVE-2026-49352
Immediate Actions Required
- Upgrade 9Router to version 0.4.44 or later without delay
- Set a strong, unique JWT_SECRET environment variable before restarting the application
- Invalidate all existing auth_token sessions and force re-authentication after upgrade
- Restrict network exposure of the 9Router dashboard to trusted management networks
Patch Information
The fix is available in 9Router release v0.4.44. The GitHub Security Advisory GHSA-jphh-m39h-6gwx documents the fix. The patch removes the hardcoded fallback and instead generates a 32-byte random secret persisted to the data directory with 0o600 permissions when JWT_SECRET is not provided.
Workarounds
- If immediate upgrade is not possible, explicitly set JWT_SECRET to a cryptographically random value of at least 32 bytes
- Place 9Router behind an authenticated reverse proxy that enforces a second authentication layer
- Block external network access to 9Router ports until the patch is applied
# Generate a strong JWT secret and configure 9Router
export JWT_SECRET="$(openssl rand -hex 32)"
# Verify the variable is set before starting the service
printenv JWT_SECRET
# Restart 9Router to apply the new secret
systemctl restart 9router
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

