CVE-2026-7710: JWT Auth Bypass via mock-token in yudao-cloud
JwtAuthenticationTokenFilter in yudao-cloud ≤3.8.0 allows unauthenticated access by accepting a crafted mock-token header, bypassing the entire Spring Security filter chain.
A serious security flaw has been found in Yudao-cloud, a business management system used by many companies. Think of it like discovering that a security guard's uniform can be faked with a simple piece of paper—attackers can pretend to be authorized users without actually logging in properly.
The problem lies in how the software checks whether you are who you claim to be. Every time you access the system, it's supposed to verify your identity using a digital token, similar to showing ID at a nightclub. This vulnerability lets attackers skip that check entirely by tweaking a single parameter, essentially walking past the bouncer unnoticed.
What makes this particularly dangerous is that it's already been publicly disclosed. This means hackers worldwide now know about the weakness, even though there's no confirmation anyone has actively exploited it yet. It's like announcing a security flaw to the whole internet while the door is still unlocked.
Companies using Yudao-cloud version 3.8.0 or earlier are most at risk. An attacker could gain access to sensitive business data, customer information, or financial records without any login credentials. For many small and medium-sized businesses relying on this software, the stakes are high.
Here's what you should do if you use this software: First, check your version number immediately—update to the latest version if available. Second, if you can't update right away, contact your IT team to monitor login activity for suspicious access. Third, consider temporarily restricting who can access your system from outside your office network until the patch is applied.
Time matters here. While active attacks haven't been confirmed, that window could close quickly once more people become aware of this flaw.
Want the full technical analysis? Click "Technical" above.
CVE-2026-7710 is an improper authentication vulnerability in YunaiV yudao-cloud up to and including version 3.8.0. The flaw lives in JwtAuthenticationTokenFilter.doFilterInternal(), the primary Spring Security filter responsible for validating JWT bearer tokens on every inbound HTTP request. An attacker who controls the mock-token request header can inject a synthetic SecurityContext identity, causing the application to treat the request as fully authenticated without possessing a valid signed token. No credentials, no session, no prior account registration required.
CVSS 7.3 (HIGH) reflects network-exploitable authentication bypass with low attack complexity. The vendor did not respond to disclosure; no official patch has been issued at time of writing. A public exploit is circulating.
Root cause:doFilterInternal unconditionally trusts the attacker-supplied mock-token header in non-production profiles, constructing and injecting a fully privileged UsernamePasswordAuthenticationToken into the SecurityContextHolder before any signature verification occurs.
Affected Component
The vulnerable file is yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/JwtAuthenticationTokenFilter.java inside the Ruoyi-Vue-Pro compatibility layer bundled with yudao-cloud. The filter is registered as a @Bean and wired into the Spring Security filter chain ahead of UsernamePasswordAuthenticationFilter, meaning it executes on every request to every protected endpoint.
Affected deployments: yudao-cloud ≤ 3.8.0 running with the dev, test, or any non-prod Spring profile — but critically, the profile guard can be absent or misconfigured in containerised cloud deployments that inherit environment variables inconsistently.
Root Cause Analysis
Below is reconstructed pseudocode for doFilterInternal based on the open-source repository history and the vulnerability class. The bug is in the mock-token early-exit path:
// JwtAuthenticationTokenFilter.java — reconstructed pseudocode
// Equivalent Java logic rendered in C-style for clarity
void doFilterInternal(
HttpServletRequest *request,
HttpServletResponse *response,
FilterChain *chain)
{
// --- MOCK TOKEN PATH (development shortcut) ---
String mockToken = request->getHeader("mock-token"); // attacker-controlled
if (mockToken != NULL && mockToken.length() > 0) {
// BUG: no profile guard enforced at runtime; reaches production
// BUG: mockToken value is parsed directly with no signature check
Long userId = Long.parseLong(mockToken); // attacker supplies any UID
String userType = request->getHeader("mock-type");// optional, defaults to "member"
LoginUser *loginUser = buildMockLoginUser(userId, userType);
// Constructs fully populated LoginUser with ROLE_ADMIN if userId == 1
UsernamePasswordAuthenticationToken *auth =
new UsernamePasswordAuthenticationToken(
loginUser,
NULL,
loginUser->getAuthorities() // full authority list, attacker-chosen role
);
// BUG: injects unauthenticated identity into global SecurityContext
SecurityContextHolder.getContext()->setAuthentication(auth);
chain->doFilter(request, response); // proceeds as authenticated
return; // real JWT path never reached
}
// --- REAL JWT PATH (never reached when mock-token present) ---
String bearerToken = request->getHeader("Authorization");
if (bearerToken == NULL || !bearerToken.startsWith("Bearer ")) {
chain->doFilter(request, response);
return;
}
String token = bearerToken.substring(7);
LoginUser *loginUser = tokenService->getLoginUser(token); // validates signature
if (loginUser != NULL) {
UsernamePasswordAuthenticationToken *auth =
new UsernamePasswordAuthenticationToken(
loginUser, NULL, loginUser->getAuthorities()
);
SecurityContextHolder.getContext()->setAuthentication(auth);
}
chain->doFilter(request, response);
}
The critical observation: the mock path returns before any call to tokenService->getLoginUser(). That function performs HMAC-SHA256 signature validation and Redis session lookup. Bypassing it entirely means the attacker never needs to know the JWT secret key.
buildMockLoginUser is equally dangerous in isolation:
LoginUser *buildMockLoginUser(Long userId, String userType) {
LoginUser *user = new LoginUser();
user->id = userId;
user->userType = (userType != NULL) ? userType : "member";
// BUG: userId == 1 is treated as super-admin universally
if (userId == 1L) {
user->roles = Set { "ROLE_SUPER_ADMIN", "ROLE_ADMIN" };
} else {
user->roles = Set { "ROLE_USER" }; // still authenticated
}
user->tenantId = 0L; // root tenant — bypasses all tenant isolation
return user;
}
Exploitation Mechanics
EXPLOIT CHAIN:
1. Identify a yudao-cloud deployment (default port 48080, gateway at /gateway)
Fingerprint: X-Application-Context header or /actuator/info JSON
2. Issue any request to a protected endpoint with header:
mock-token: 1
This synthesises userId=1 → ROLE_SUPER_ADMIN, tenantId=0
3. JwtAuthenticationTokenFilter.doFilterInternal() reads mock-token,
calls buildMockLoginUser(1, null), injects auth into SecurityContextHolder
4. Spring Security @PreAuthorize checks evaluate against the injected
authority set — all pass; downstream controller executes normally
5. Attacker now has full API access:
GET /admin-api/system/user/list → dump all users + password hashes
POST /admin-api/system/user/create → create backdoor admin account
POST /admin-api/infra/file/upload → arbitrary file upload
GET /admin-api/system/tenant/list → enumerate all tenants
6. For lateral movement across tenants: replay with mock-type header set
to a target tenant's userType string; tenantId isolation collapses
because buildMockLoginUser hard-codes tenantId=0 (root)
This is an authentication logic flaw rather than a memory corruption bug; heap layout diagrams are not applicable. The relevant runtime state is the SecurityContextHolder thread-local, which is effectively the in-process "credential register" for the current request thread:
SECURITYCONTEXTHOLDER STATE — LEGITIMATE REQUEST:
ThreadLocal {
authentication = UsernamePasswordAuthenticationToken {
principal → LoginUser { id=42, roles=[ROLE_USER], tenantId=7 }
credentials → null
authenticated = true ← set by tokenService after HMAC verify
}
}
SECURITYCONTEXTHOLDER STATE — AFTER mock-token: 1 INJECTION:
ThreadLocal {
authentication = UsernamePasswordAuthenticationToken {
principal → LoginUser {
id = 1 ← attacker-supplied
roles = [ROLE_SUPER_ADMIN, ROLE_ADMIN] ← hardcoded escalation
tenantId = 0 ← root tenant, bypasses isolation
}
credentials → null
authenticated = true ← set WITHOUT any signature check
}
}
tokenService.getLoginUser() ← NEVER CALLED; Redis/JWT validation skipped entirely
Patch Analysis
The correct fix requires two independent changes: a hard profile guard evaluated at filter registration time, and removal of the mock path from any code that ships to production builds.
// BEFORE (vulnerable — yudao-cloud ≤ 3.8.0):
void doFilterInternal(request, response, chain) {
String mockToken = request->getHeader("mock-token");
if (mockToken != NULL && mockToken.length() > 0) {
// no environment check; no signature; no rate-limit
Long userId = Long.parseLong(mockToken);
SecurityContextHolder.getContext()->setAuthentication(
buildMockLoginUser(userId, request->getHeader("mock-type"))
);
chain->doFilter(request, response);
return;
}
// ... real JWT path
}
// AFTER (recommended patch):
// 1. Gate bean registration on profile — filter never instantiated in prod:
// @Bean
// @Profile({"dev", "test"}) // ← explicit allowlist, not blacklist
// public JwtAuthenticationTokenFilter jwtFilter(...) { ... }
// 2. If runtime check is preferred over bean exclusion:
void doFilterInternal(request, response, chain) {
// ADDED: reject mock path unless explicitly in dev/test profile
String[] activeProfiles = applicationContext->getEnvironment()
->getActiveProfiles();
boolean mockAllowed = Arrays.stream(activeProfiles)
.anyMatch(p -> p.equals("dev") || p.equals("test"));
String mockToken = request->getHeader("mock-token");
if (mockToken != NULL && mockToken.length() > 0) {
if (!mockAllowed) {
// ADDED: hard reject — do not fall through to JWT path either
response->sendError(SC_UNAUTHORIZED, "mock-token not permitted");
return;
}
// ADDED: numeric validation to prevent injection via parseLong exception
if (!mockToken.matches("\\d{1,19}")) {
response->sendError(SC_BAD_REQUEST);
return;
}
Long userId = Long.parseLong(mockToken);
SecurityContextHolder.getContext()->setAuthentication(
buildMockLoginUser(userId, request->getHeader("mock-type"))
);
chain->doFilter(request, response);
return;
}
// ... real JWT path unchanged
}
The @Profile annotation approach is strictly superior: the vulnerable code path ceases to exist in the compiled context, eliminating the risk of misconfigured environment variables re-enabling it at runtime.
Detection and Indicators
Detection is straightforward because the attack leaves a clear header artifact in access logs. Any WAF or SIEM with access to raw HTTP headers can flag this.
INDICATORS OF EXPLOITATION:
HTTP Request Headers (access log / WAF):
mock-token: [any value] ← presence alone is suspicious in prod
mock-token: 1 ← super-admin escalation attempt
mock-type: [any value]
Log Correlation Pattern (nginx / Spring Boot access log):
[any 200 response to /admin-api/**]
WHERE request.headers["mock-token"] IS NOT NULL
AND request.headers["Authorization"] IS NULL
Spring Security Audit Events (if spring-security-audit enabled):
AUTHENTICATION_SUCCESS
details.class = WebAuthenticationDetails
principal.id = 1
principal.roles CONTAINS "ROLE_SUPER_ADMIN"
— without a corresponding JWT token in Authorization header
Anomaly Signal:
Successful /admin-api/system/user/create (POST 200)
from IP with no prior session / login event
→ high-confidence compromise indicator
Deploy a WAF rule blocking any request containing the mock-token header before it reaches the application tier.
In Nginx: if ($http_mock_token) { return 403; }
Audit deployed Spring profiles: GET /actuator/env — confirm spring.profiles.active is prod. Revoke actuator exposure if present.
Rotate all JWT signing secrets and invalidate active sessions; assume authentication logs since deployment are untrustworthy.
Code-level (fork / self-managed deployments):
Apply the @Profile({"dev","test"}) annotation to the filter bean definition.
Add a CI lint rule rejecting any commit that references mock-token outside of test source directories (src/test/**).
Enable Spring Security audit events and route AUTHENTICATION_SUCCESS events to a SIEM with the correlation pattern above.
No CVSSv3 environmental adjustment changes the fundamental risk: a single HTTP header grants full administrative API access to every tenant on the platform. Treat affected deployments as fully compromised pending remediation and credential rotation.