Description
CORS controls when a browser may read a response from another origin. Reflecting an unvalidated Origin value in Access-Control-Allow-Origin can let an untrusted site read the response. Exposure of authenticated responses also depends on browser credential delivery, cookie policies and the server's credential settings. CORS permission alone does not send session cookie values to the other site.
Potential impact
- An untrusted site may read sensitive API responses.
- When credentialed requests are permitted, data returned with the victim's permissions may be exposed.
- CORS does not replace authentication, authorization or CSRF protection; these controls remain necessary.
Remediation
- Maintain an explicit allow-list of permitted origin schemes, hosts and ports.
- Do not reflect unvalidated input with code such as
HttpServletResponse.setHeader("Access-Control-Allow-Origin", userInput). Distinguish intentional*access to public responses from sensitive responses. - If response headers vary by origin and responses are cached, also set
Vary: Origin. Allow credentials only for origins that need them, and retain authentication, authorization and CSRF protection.
Examples
Replace the allowed origins with those used by the service. These excerpts set response headers; the application must separately configure preflight and credential policies.
Before
java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class InsecureCORS {
public void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
String origin = request.getHeader("Origin"); // Set from user input
response.setHeader("Access-Control-Allow-Origin", origin); // Unvalidated origin
}
}
After
java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Set;
public class SecureCORS {
private static final Set<String> ALLOWED_ORIGINS = Set.of(
"https://example.com",
"https://sub1.example.com",
"https://sub2.example.com"
);
public void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
String origin = request.getHeader("Origin");
if (origin != null && ALLOWED_ORIGINS.contains(origin)) {
response.setHeader("Access-Control-Allow-Origin", origin);
} else {
response.setHeader("Access-Control-Allow-Origin", "https://example.com"); // Default permitted origin
}
}
}
Explanation:
- Before: Reflects user input directly in
Access-Control-Allow-Origin, allowing an attacker-controlled origin. - After: Reflects an origin only when it is allow-listed. Otherwise it returns a fixed origin, so a request from a different origin cannot read the response.