Secure and properly configure HTTP headers to prevent cross-origin resource sharing (CORS) vulnerabilities.
import javax.servlet.*;
import javax.servlet.http.*;
public class VulnerableServlet extends HttpServlet {
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.addHeader("Access-Control-Allow-Origin", "*");
resp.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS, HEAD");
resp.addHeader("Access-Control-Allow-Headers", "Content-Type");
resp.addHeader("Access-Control-Max-Age", "86400");
}
}
This code is a simple Java servlet that handles HTTP OPTIONS requests, which are used in CORS (Cross-Origin Resource Sharing) preflight checks.
The vulnerability lies in the
Access-Control-Allow-Origin
header. This header is used by the browser to determine if a web page from a different domain can perform a CORS request to the server. In the code above, the server responds with
Access-Control-Allow-Origin: *
, which means that any domain is allowed to perform CORS requests. This is a security risk because it allows any website to interact with your server, which could potentially lead to data leaks or other security issues.
Similarly, the
Access-Control-Allow-Methods
header is set to allow all HTTP methods, and the
Access-Control-Allow-Headers
header is set to allow the
Content-Type
header. This means that any CORS request, regardless of the HTTP method or the headers used, will be allowed. This could potentially allow an attacker to perform actions that should not be allowed from a different domain.
The
Access-Control-Max-Age
header is set to a large value, which means that the browser will cache the preflight response for a long time. This could potentially increase the impact of an attack, as the browser will not perform a new preflight request for a long time, even if the server's CORS policy changes.
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class SecureServlet extends HttpServlet {
private static final Set ALLOWED_ORIGINS = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList("http://example1.com", "http://example2.com")));
private static final Set ALLOWED_METHODS = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList("GET", "POST", "OPTIONS")));
private static final Set ALLOWED_HEADERS = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList("Content-Type")));
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String origin = req.getHeader("Origin");
if (ALLOWED_ORIGINS.contains(origin)) {
resp.addHeader("Access-Control-Allow-Origin", origin);
}
String method = req.getHeader("Access-Control-Request-Method");
if (ALLOWED_METHODS.contains(method)) {
resp.addHeader("Access-Control-Allow-Methods", method);
}
String headers = req.getHeader("Access-Control-Request-Headers");
if (headers != null) {
String[] requestedHeaders = headers.split(",");
for (String requestedHeader : requestedHeaders) {
if (ALLOWED_HEADERS.contains(requestedHeader.trim())) {
resp.addHeader("Access-Control-Allow-Headers", requestedHeader);
}
}
}
resp.addHeader("Access-Control-Max-Age", "3600");
}
}
The above code fixes the vulnerability by:
1. Removing the wildcard (*) from the 'Access-Control-Allow-Origin' header and specifying the allowed origins explicitly. The allowed origins are stored in a Set for easy lookup.
2. Validating the 'Origin' header in the request to ensure it matches the allowed origins. If the origin is allowed, it is added to the 'Access-Control-Allow-Origin' header.
3. Removing unnecessary HTTP methods from the 'Access-Control-Allow-Methods' header and validating the requested method in the 'Access-Control-Request-Method' header against the allowed methods. If the method is allowed, it is added to the 'Access-Control-Allow-Methods' header.
4. Removing unnecessary headers from the 'Access-Control-Allow-Headers' header and validating the requested headers in the 'Access-Control-Request-Headers' header against the allowed headers. If a header is allowed, it is added to the 'Access-Control-Allow-Headers' header.
5. Setting a reasonable value for the 'Access-Control-Max-Age' header to limit the preflight request caching time. In this case, the caching time is set to 1 hour (3600 seconds).