Skip to content

Commit

Permalink
Ensure Forwarded and X-Forwarded values are the same
Browse files Browse the repository at this point in the history
  • Loading branch information
sberyozkin committed Nov 20, 2024
1 parent a14d4c7 commit a46c3b2
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,72 @@ public class AllowBothForwardedHeadersTest {
"application.properties"));

@Test
public void test() {
public void testHeaderValuesMatch() {
assertThat(RestAssured.get("/path").asString()).startsWith("http|");

RestAssured.given()
.header("Forwarded", "proto=http;for=backend2:5555;host=somehost2")
.header("Forwarded", "proto=https;for=backend2:5555;host=somehost2")
.header("X-Forwarded-Proto", "https")
.header("X-Forwarded-For", "backend:4444")
.header("X-Forwarded-Server", "somehost")
.header("X-Forwarded-For", "backend2:5555")
.header("X-Forwarded-Server", "somehost2")
.get("/path")
.then()
.body(Matchers.equalTo("http|somehost2|backend2:5555|/path|http://somehost2/path"));
.body(Matchers.equalTo("https|somehost2|backend2:5555|/path|https://somehost2/path"));
}

@Test
public void testProtoDoesNotMatch() {
assertThat(RestAssured.get("/path").asString()).startsWith("http|");

RestAssured.given()
.header("Forwarded", "proto=https;for=backend2:5555;host=somehost2")
.header("X-Forwarded-Proto", "http")
.header("X-Forwarded-For", "backend2:5555")
.header("X-Forwarded-Server", "somehost2")
.get("/path")
.then()
.statusCode(400);
}

@Test
public void testForHostDoesNotMatch() {
assertThat(RestAssured.get("/path").asString()).startsWith("http|");

RestAssured.given()
.header("Forwarded", "proto=https;for=backend:5555;host=somehost2")
.header("X-Forwarded-Proto", "http")
.header("X-Forwarded-For", "backend2:5555")
.header("X-Forwarded-Server", "somehost2")
.get("/path")
.then()
.statusCode(400);
}

@Test
public void testForHostPortDoesNotMatch() {
assertThat(RestAssured.get("/path").asString()).startsWith("http|");

RestAssured.given()
.header("Forwarded", "proto=https;for=backend2:4444;host=somehost2")
.header("X-Forwarded-Proto", "http")
.header("X-Forwarded-For", "backend2:5555")
.header("X-Forwarded-Server", "somehost2")
.get("/path")
.then()
.statusCode(400);
}

@Test
public void testHostDoesNotMatch() {
assertThat(RestAssured.get("/path").asString()).startsWith("http|");

RestAssured.given()
.header("Forwarded", "proto=https;for=backend2:4444;host=somehost")
.header("X-Forwarded-Proto", "http")
.header("X-Forwarded-For", "backend2:5555")
.header("X-Forwarded-Server", "somehost2")
.get("/path")
.then()
.statusCode(400);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,59 +132,88 @@ private void calculate() {
boolean isProxyAllowed = trustedProxyCheck.isProxyAllowed();
if (isProxyAllowed) {
String forwarded = delegate.getHeader(FORWARDED);
if (forwardingProxyOptions.allowForwarded && forwarded != null) {
boolean forwardingAllowed = forwardingProxyOptions.allowForwarded && forwarded != null;

ForwardedValues forwardedValues = null;

if (forwardingAllowed) {
forwardedValues = new ForwardedValues();

Matcher matcher = FORWARDED_PROTO_PATTERN.matcher(forwarded);
if (matcher.find()) {
scheme = (matcher.group(1).trim());
port = -1;
forwardedValues.forwardedScheme = matcher.group(1).trim();
forwardedValues.forwardedPort = -1;
scheme = forwardedValues.forwardedScheme;
port = forwardedValues.forwardedPort;
}

matcher = FORWARDED_HOST_PATTERN.matcher(forwarded);
if (matcher.find()) {
setHostAndPort(matcher.group(1).trim(), port);
forwardedValues.forwardedHost = host;
forwardedValues.forwardedPort = port;
}

matcher = FORWARDED_FOR_PATTERN.matcher(forwarded);
if (matcher.find()) {
remoteAddress = parseFor(matcher.group(1).trim(), remoteAddress != null ? remoteAddress.port() : port);
forwardedValues.forwardedRemoteHost = remoteAddress.host();
forwardedValues.forwardedRemotePort = remoteAddress.port();
}
} else if (forwardingProxyOptions.allowXForwarded) {
}

if (forwardingProxyOptions.allowXForwarded) {

XForwardedValues xForwardedValues = new XForwardedValues();

String protocolHeader = delegate.getHeader(X_FORWARDED_PROTO);
if (protocolHeader != null) {
scheme = getFirstElement(protocolHeader);
port = -1;
xForwardedValues.xForwardedScheme = getFirstElement(protocolHeader);
scheme = xForwardedValues.xForwardedScheme;
xForwardedValues.xForwardedPort = -1;
port = xForwardedValues.xForwardedPort;
}

String forwardedSsl = delegate.getHeader(X_FORWARDED_SSL);
boolean isForwardedSslOn = forwardedSsl != null && forwardedSsl.equalsIgnoreCase("on");
if (isForwardedSslOn) {
scheme = HTTPS_SCHEME;
port = -1;
xForwardedValues.xForwardedScheme = HTTPS_SCHEME;
scheme = xForwardedValues.xForwardedScheme;
xForwardedValues.xForwardedPort = -1;
port = xForwardedValues.xForwardedPort;
}

if (forwardingProxyOptions.enableForwardedHost) {
String hostHeader = delegate.getHeader(forwardingProxyOptions.forwardedHostHeader);
if (hostHeader != null) {
setHostAndPort(getFirstElement(hostHeader), port);
xForwardedValues.xForwardedHost = host;
xForwardedValues.xForwardedPort = port;
}
}

if (forwardingProxyOptions.enableForwardedPrefix) {
String prefixHeader = delegate.getHeader(forwardingProxyOptions.forwardedPrefixHeader);
if (prefixHeader != null) {
uri = appendPrefixToUri(prefixHeader, uri);
xForwardedValues.xForwardedUri = appendPrefixToUri(prefixHeader, uri);
uri = xForwardedValues.xForwardedUri;
}
}

String portHeader = delegate.getHeader(X_FORWARDED_PORT);
if (portHeader != null) {
port = parsePort(getFirstElement(portHeader), port);
xForwardedValues.xForwardedPort = parsePort(getFirstElement(portHeader), port);
port = xForwardedValues.xForwardedPort;
}

String forHeader = delegate.getHeader(X_FORWARDED_FOR);
if (forHeader != null) {
remoteAddress = parseFor(getFirstElement(forHeader), remoteAddress != null ? remoteAddress.port() : port);
xForwardedValues.xForwardedRemoteHost = remoteAddress.host();
xForwardedValues.xForwardedRemotePort = remoteAddress.port();
}

checkForwardedAndXForwardedMismatch(forwardedValues, xForwardedValues);
}
}

Expand Down Expand Up @@ -299,4 +328,39 @@ private String stripSlashes(String uri) {
return result;
}

private class ForwardedValues {
String forwardedScheme = scheme == null ? "" : scheme;
String forwardedHost = host == null ? "" : host;
int forwardedPort = port;
String forwardedUri = uri == null ? "" : uri;
String forwardedRemoteHost = "";
int forwardedRemotePort = -1;
}

private class XForwardedValues {
String xForwardedScheme = scheme == null ? "" : scheme;
String xForwardedHost = host == null ? "" : host;
int xForwardedPort = port;
String xForwardedUri = uri == null ? "" : uri;
String xForwardedRemoteHost = "";
int xForwardedRemotePort = -1;
}

private void checkForwardedAndXForwardedMismatch(ForwardedValues fw, XForwardedValues xfw) {
if (fw == null || xfw == null) {
return;
}
boolean match = fw.forwardedScheme.equals(xfw.xForwardedScheme)
&& fw.forwardedHost.equals(xfw.xForwardedHost)
&& fw.forwardedPort == xfw.xForwardedPort
&& fw.forwardedUri.equals(xfw.xForwardedUri)
&& fw.forwardedRemoteHost.equals(xfw.xForwardedRemoteHost)
&& fw.forwardedRemotePort == xfw.xForwardedRemotePort;

if (!match) {
log.warn("Forwarded and X-Forwarded header values do not match");
delegate.response().setStatusCode(400);
delegate.end();
}
}
}

0 comments on commit a46c3b2

Please sign in to comment.