Skip to content

Commit 1b1b691

Browse files
author
Jesper Steen Møller
committed
GH-37114: Control fallback port when omitted in "Forwarded:for=xxx"
- Parsers in `ForwardedHeaderUtils` now return `0` as the unknown client port when it was not supplied by the proxy and no `remoteAddress` was supplied. Signed-off-by: Jesper Steen Møller <jesper@selskabet.org>
1 parent 51c4539 commit 1b1b691

2 files changed

Lines changed: 48 additions & 12 deletions

File tree

spring-web/src/main/java/org/springframework/web/util/ForwardedHeaderUtils.java

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ public abstract class ForwardedHeaderUtils {
6565
* Parse the "Forwarded" header.
6666
* @param uri the request {@code URI}
6767
* @param headers the HTTP headers to get the "Forwarded" header from
68-
* @param remoteAddress for a default port for the parsed "for" value
69-
* @param localAddress for a default port for the parsed "by" value
68+
* @param remoteAddress for a default port for the parsed "for" value.
69+
* If null and no port is specified in the header, 0 is returned.
70+
* @param localAddress for a default port for the parsed "by" value.
71+
* If null, the port indication from the 'uri' is used as the 'byAddress'.
7072
* @return a {@link ForwardedInfo} with the parse results
7173
* @since 6.1.29
7274
* @see <a href="https://tools.ietf.org/html/rfc7239">RFC 7239</a>
@@ -95,7 +97,7 @@ public static ForwardedInfo parseStandardHeader(URI uri, HttpHeaders headers,
9597

9698
String forValue = pairs.get("for");
9799
if (forValue != null) {
98-
forAddress = parseInetSocketAddress(forValue, getPortToUse(remoteAddress, uri));
100+
forAddress = parseInetSocketAddress(forValue, getRemotePortFallback(remoteAddress));
99101
}
100102
String byValue = pairs.get("by");
101103
if (byValue != null) {
@@ -220,6 +222,10 @@ private static int getPortToUse(@Nullable InetSocketAddress address, URI uri) {
220222
return (address != null ? address.getPort() : "https".equals(uri.getScheme()) ? 443 : 80);
221223
}
222224

225+
private static int getRemotePortFallback(@Nullable InetSocketAddress address) {
226+
return (address != null ? address.getPort() : 0);
227+
}
228+
223229
private static InetSocketAddress parseInetSocketAddress(String value, int port) {
224230
String host = value;
225231
int portSeparatorIdx = value.lastIndexOf(':');
@@ -247,7 +253,8 @@ private static InetSocketAddress parseInetSocketAddress(String value, int port)
247253
* {@code byAddress} in {@link ForwardedInfo} is always {@code null}.
248254
* @param uri the request {@code URI}
249255
* @param headers the HTTP headers to get the "Forwarded" header from
250-
* @param remoteAddress for a default port for the parsed "for" value
256+
* @param remoteAddress for a default port for the parsed "for" value.
257+
* If null, port 0 is returned if not specified in the header.
251258
* @param localAddress for a default port for the parsed "by" value;
252259
* this argument is ignored currently and the byAddress is always {@code null}
253260
* @return a {@link ForwardedInfo} with the scheme, host, and port adapted
@@ -289,8 +296,7 @@ else if (isForwardedSslOn(headers)) {
289296
String host = getLeftMostValue(forHeader);
290297
boolean ipv6 = (host.indexOf(':') != -1);
291298
host = (ipv6 && !host.startsWith("[") && !host.endsWith("]") ? "[" + host + "]" : host);
292-
int port = getPortToUse(remoteAddress, uri);
293-
forAddress = InetSocketAddress.createUnresolved(host, port);
299+
forAddress = InetSocketAddress.createUnresolved(host, getRemotePortFallback(remoteAddress));
294300
}
295301

296302
return new ForwardedInfo(uriComponentsBuilder, forAddress, null);
@@ -371,11 +377,13 @@ else if (isForwardedSslOn(headers)) {
371377
/**
372378
* Parse the first "Forwarded: for=..." or "X-Forwarded-For" header value to
373379
* an {@code InetSocketAddress} representing the address of the client.
374-
* @param uri the request {@code URI}
380+
* @param uri the request {@code URI} (not used)
375381
* @param headers the request headers that may contain forwarded headers
376382
* @param remoteAddress the current remote address
377383
* @return an {@code InetSocketAddress} with the extracted host and port, or
378-
* {@code null} if the headers are not present
384+
* {@code null} if the headers are not present. If the address is present but
385+
* the port is omitted in the header, the port is taken from {@code remoteAddress}
386+
* if given, otherwise 0 is used.
379387
* @deprecated as of 7.1 in favor of {@link #parseStandardHeader} and
380388
* {@link #parseXForwardedHeaders}
381389
*/
@@ -389,7 +397,7 @@ else if (isForwardedSslOn(headers)) {
389397
Matcher matcher = FORWARDED_FOR_PATTERN.matcher(forwardedToUse);
390398
if (matcher.find()) {
391399
String value = matcher.group(1).trim();
392-
return parseInetSocketAddress(value, getPortToUse(remoteAddress, uri));
400+
return parseInetSocketAddress(value, getRemotePortFallback(remoteAddress));
393401
}
394402
}
395403

@@ -398,7 +406,7 @@ else if (isForwardedSslOn(headers)) {
398406
String host = getLeftMostValue(forHeader);
399407
boolean ipv6 = (host.indexOf(':') != -1);
400408
host = (ipv6 && !host.startsWith("[") && !host.endsWith("]") ? "[" + host + "]" : host);
401-
return InetSocketAddress.createUnresolved(host, getPortToUse(remoteAddress, uri));
409+
return InetSocketAddress.createUnresolved(host, getRemotePortFallback(remoteAddress));
402410
}
403411

404412
return null;
@@ -411,7 +419,8 @@ else if (isForwardedSslOn(headers)) {
411419
* @param headers the request headers that may contain forwarded headers
412420
* @param localAddress the current local address
413421
* @return an {@code InetSocketAddress} with the extracted host and port, or
414-
* {@code null} if the headers are not present
422+
* {@code null} if the headers are not present. A port number of '0' indicates
423+
* that the port is unspecified.
415424
* @since 7.0
416425
* @deprecated as of 7.1 in favor of {@link #parseStandardHeader} and
417426
* {@link #parseXForwardedHeaders}

spring-web/src/test/java/org/springframework/web/util/ForwardedHeaderUtilsTests.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,7 @@ void fromHttpRequestForwardedHeaderComma() {
535535

536536
assertThat(info.forAddress()).isNotNull();
537537
assertThat(info.forAddress().getHostString()).isEqualTo("192.0.2.0");
538+
assertThat(info.forAddress().getPort()).isEqualTo(0);
538539

539540
UriComponents result = info.uriComponentsBuilder().build();
540541
assertThat(result.getScheme()).isEqualTo("http");
@@ -543,15 +544,41 @@ void fromHttpRequestForwardedHeaderComma() {
543544
assertThat(result.getPort()).isEqualTo(8080);
544545
}
545546

547+
@Test
548+
void fromHttpRequestForwardedWithPort() {
549+
MockHttpServletRequest request = new MockHttpServletRequest();
550+
request.addHeader("Forwarded", "for=192.0.2.1:1920");
551+
552+
HttpRequest httpRequest = new ServletServerHttpRequest(request);
553+
ForwardedHeaderUtils.ForwardedInfo info =
554+
ForwardedHeaderUtils.parseStandardHeader(httpRequest.getURI(), httpRequest.getHeaders(), null, null);
555+
556+
assertThat(info.forAddress()).isNotNull();
557+
assertThat(info.forAddress().getHostString()).isEqualTo("192.0.2.1");
558+
assertThat(info.forAddress().getPort()).isEqualTo(1920);
559+
}
560+
546561
@Test // gh-34253
547562
void fromHttpRequestXForwardedHeaderForIpv6Formatting() {
548563
HttpHeaders headers = new HttpHeaders();
549564
headers.add("X-Forwarded-For", "fd00:fefe:1::4, 192.168.0.1");
550565

551566
InetSocketAddress address = ForwardedHeaderUtils.parseXForwardedHeaders(
552-
URI.create("https://example.com"), headers, null, null).forAddress();
567+
URI.create("https://example.com"), headers, InetSocketAddress.createUnresolved("host.not.used", 42), null).forAddress();
553568

554569
assertThat(address.getHostName()).isEqualTo("[fd00:fefe:1::4]");
570+
assertThat(address.getPort()).isEqualTo(42);
571+
}
572+
573+
@Test
574+
void parseForwardedForDeprecated() {
575+
HttpHeaders headers = new HttpHeaders();
576+
headers.add("X-Forwarded-For", "192.168.0.1");
577+
578+
InetSocketAddress address = ForwardedHeaderUtils.parseForwardedFor(URI.create("https://example.com"), headers, null);
579+
580+
assertThat(address.getHostName()).isEqualTo("192.168.0.1");
581+
assertThat(address.getPort()).isEqualTo(0);
555582
}
556583

557584
@Test

0 commit comments

Comments
 (0)