Description
Response splitting can occur when carriage returns (\r, CR) or line feeds (\n, LF) from input reach an HTTP response header and a server, proxy or recipient interprets them as message boundaries. Injected headers or responses can lead to cache poisoning or XSS.
Some servers reject or clean invalid header values, but do not assume every component in the deployment does so. Keep container validation enabled and check field syntax in the application.
Potential impact
- Manipulated responses that alter browser behavior
- Poisoned caches serving malicious content
- XSS if the recipient interprets injected response content as executable markup
Remediation
- Avoid placing untrusted input directly in headers. Prefer predefined values.
- Reject values containing CR, LF or other controls that the header does not permit.
- If a user must select a header value, map that choice to an approved set and validate the field's specific syntax.
Examples
Before
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class InsecureResponseSplitting {
public void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
String userInput = request.getParameter("input"); // Pass user input directly to the header
response.setHeader("Custom-Header", userInput); // Unvalidated input
}
}
After
This custom header accepts at most 128 printable ASCII characters. Validate any additional requirements of the actual header as well.
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SecureResponseSplitting {
public void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
String userInput = request.getParameter("input");
if (!isValidHeaderValue(userInput)) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid input");
return;
}
response.setHeader("Custom-Header", userInput); // Use only the validated value
}
private boolean isValidHeaderValue(String value) {
return value != null
&& value.length() <= 128
&& value.chars().allMatch(c -> c >= 0x20 && c <= 0x7e);
}
}
The first example passes input directly to setHeader(); it can be dangerous if the receiving path accepts CR/LF as boundaries. The second uses isValidHeaderValue() to reject controls and passes only validated values. Java string escaping is not an HTTP-header defense.