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 21, 2024
1 parent a14d4c7 commit 6afc82d
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,108 @@ public class AllowBothForwardedHeadersTest {
"application.properties"));

@Test
public void test() {
public void testAllHeaderValuesMatch() {
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 tesProtoHeaderValuesMatch() {
assertThat(RestAssured.get("/path").asString()).startsWith("http|");

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

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

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

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

RestAssured.given()
.header("Forwarded", "proto=https;for=backend2:5555;host=somehost2")
.header("X-Forwarded-Server", "somehost2")
.get("/path")
.then()
.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 @@ -19,6 +19,10 @@

package io.quarkus.vertx.http.runtime;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -132,40 +136,63 @@ 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;

Forwarded forwardedValues = null;

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

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

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

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

if (forwardingProxyOptions.allowXForwarded) {

Forwarded xForwardedValues = new Forwarded();

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

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

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

Expand All @@ -179,12 +206,22 @@ private void calculate() {
String portHeader = delegate.getHeader(X_FORWARDED_PORT);
if (portHeader != null) {
port = parsePort(getFirstElement(portHeader), port);
xForwardedValues.setPort(port);
}

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

if (forwardedValues != null && !xForwardedValues.modifiedAddressPropertiesMatch(forwardedValues)) {
delegate.response().setStatusCode(400);
delegate.end();
return;
}

}
}

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

static class Forwarded {
private static String SCHEME = "scheme";
private static String HOST = "host";
private static String PORT = "port";
private static String REMOTE_HOST = "remote host";
private static String REMOTE_PORT = "remote port";

private Map<String, Object> forwarded = new HashMap<>();

public void setScheme(String scheme) {
forwarded.put(SCHEME, scheme);
}

public void setHost(String host) {
forwarded.put(HOST, host);
}

public void setPort(Integer port) {
forwarded.put(PORT, port);
}

public void setRemoteHost(String host) {
forwarded.put(REMOTE_HOST, host);
}

public void setRemotePort(Integer port) {
forwarded.put(REMOTE_PORT, port);
}

public boolean modifiedAddressPropertiesMatch(Forwarded fw) {
Set<String> keys = new HashSet<>(forwarded.keySet());
keys.retainAll(fw.forwarded.keySet());

for (String key : keys) {
if (!forwarded.get(key).equals(fw.forwarded.get(key))) {
log.debugf("Forwarded and X-Forwarded %s values do not match", key);
return false;
}
}

return true;

}
}
}

0 comments on commit 6afc82d

Please sign in to comment.