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 9350107
Show file tree
Hide file tree
Showing 2 changed files with 139 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,58 +132,89 @@ 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(scheme, host, port, uri);

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) {

ForwardedValues xForwardedValues = new ForwardedValues(scheme, host, port, uri);

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

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

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

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

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

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

if (forwardedValues != null) {
checkForwardedAndXForwardedValues(forwardedValues, xForwardedValues);
}
}
}
Expand Down Expand Up @@ -299,4 +330,41 @@ private String stripSlashes(String uri) {
return result;
}

private static class ForwardedValues {
String forwardedScheme;
String forwardedHost;
int forwardedPort;
String forwardedUri;
String forwardedRemoteHost;
int forwardedRemotePort;

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

@Override
public boolean equals(Object obj) {
ForwardedValues fw = (ForwardedValues) obj;

return this.forwardedScheme.equals(fw.forwardedScheme)
&& this.forwardedHost.equals(fw.forwardedHost)
&& this.forwardedPort == fw.forwardedPort
&& this.forwardedUri.equals(fw.forwardedUri)
&& this.forwardedRemoteHost.equals(fw.forwardedRemoteHost)
&& this.forwardedRemotePort == fw.forwardedRemotePort;
}
}

private void checkForwardedAndXForwardedValues(ForwardedValues fw, ForwardedValues xfw) {
if (!fw.equals(xfw)) {
log.warn("Forwarded and X-Forwarded header values do not match");
delegate.response().setStatusCode(400);
delegate.end();
}
}
}

0 comments on commit 9350107

Please sign in to comment.