diff --git a/jetty-documentation/src/main/asciidoc/old_docs/contributing/security.adoc b/jetty-documentation/src/main/asciidoc/old_docs/contributing/security.adoc
deleted file mode 100644
index b8eb4bef1cc..00000000000
--- a/jetty-documentation/src/main/asciidoc/old_docs/contributing/security.adoc
+++ /dev/null
@@ -1,32 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
-//
-// This program and the accompanying materials are made available under
-// the terms of the Eclipse Public License 2.0 which is available at
-// https://www.eclipse.org/legal/epl-2.0
-//
-// This Source Code may also be made available under the following
-// Secondary Licenses when the conditions for such availability set
-// forth in the Eclipse Public License, v. 2.0 are satisfied:
-// the Apache License v2.0 which is available at
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
-// ========================================================================
-//
-
-[[security-reporting]]
-=== Reporting Security Issues
-
-There are a number of avenues for reporting security issues to the Jetty project available.
-If the issue is directly related to Jetty itself then reporting to the Jetty developers is encouraged.
-The most direct method is to mail _security@webtide.com_.
-Since Webtide is comprised of the active committers of the Jetty project this is our preferred reporting method.
-We are generally flexible in how we work with reporters of security issues but we reserve the right to act in the interests of the Jetty project in all circumstances.
-
-If the issue is related to Eclipse or its Jetty integration then we encourage you to reach out to _security@eclipse.org_.
-
-If the issue is related to integrations with Jetty we are happy to work with you to identify the proper entity and either of the approaches above is fine.
-
-We prefer that security issues are reported directly to Jetty developers as opposed through GitHub Issues since it has no facility to tag issues as _private_.
diff --git a/jetty-documentation/src/main/asciidoc/old_docs/troubleshooting/security-reports.adoc b/jetty-documentation/src/main/asciidoc/old_docs/troubleshooting/security-reports.adoc
index cba06c71ed2..c39d6c770ab 100644
--- a/jetty-documentation/src/main/asciidoc/old_docs/troubleshooting/security-reports.adoc
+++ b/jetty-documentation/src/main/asciidoc/old_docs/troubleshooting/security-reports.adoc
@@ -19,147 +19,21 @@
[[security-reports]]
=== Jetty Security Reports
-The following sections provide information about Jetty security issues.
+==== List of Security Reports
-If you would like to report a security issue please follow these link:#security-reporting[instructions].
+A current list of Jetty security reports can be viewed on the link:https://www.eclipse.org/jetty/security-reports.htmlhttps://www.eclipse.org/jetty/security-reports.html[Project Home Page.]
-.Resolved Issues
-[width="99%",cols="11%,19%,14%,9%,14%,14%,19%",options="header",]
-|=======================================================================
-|yyyy/mm/dd |ID |Exploitable |Severity |Affects |Fixed Version |Comment
+==== Reporting Security Issues
-|2019/08/13 |CVE-2019-9518 |Med |Med |< = 9.4.20 |9.4.21
-|https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9518[Some HTTP/2 implementations are vulnerable to a flood of empty frames, potentially leading to a denial of service.]
+There are a number of avenues for reporting security issues to the Jetty project available.
-|2019/08/13 |CVE-2019-9516 |Med |Med |< = 9.4.20 |9.4.21
-|https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9516[Some HTTP/2 implementations are vulnerable to a header leak, potentially leading to a denial of service.]
+If the issue is directly related to Jetty itself then reporting to the Jetty developers is encouraged.
+The most direct method is to mail _security@webtide.com_.
+Since Webtide is comprised of the active committers of the Jetty project this is our preferred reporting method.
+We are generally flexible in how we work with reporters of security issues but we reserve the right to act in the interests of the Jetty project in all circumstances.
-|2019/08/13 |CVE-2019-9515 |Med |Med |< = 9.4.20 |9.4.21
-|https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9515[Some HTTP/2 implementations are vulnerable to a settings flood, potentially leading to a denial of service when an attacker sent a stream of SETTINGS frames to the peer.]
+If the issue is related to Eclipse or its Jetty integration then we encourage you to reach out to _security@eclipse.org_.
-|2019/08/13 |CVE-2019-9514 |Med |Med |< = 9.4.20 |9.4.21
-|https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9514[Some HTTP/2 implementations are vulnerable to a reset flood, potentially leading to a denial of service.]
+If the issue is related to integrations with Jetty we are happy to work with you to identify the proper entity and either of the approaches above is fine.
-|2019/08/13 |CVE-2019-9512 |Low |Low |< = 9.4.20 |9.4.21
-|https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9512[Some HTTP/2 implementations are vulnerable to ping floods which could lead to a denial of service.]
-
-|2019/08/13 |CVE-2019-9511 |Low |Low |< = 9.4.20 |9.4.21
-|https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9511[Some HTTP/2 implementations are vulnerable to window size manipulation and stream prioritization manipulation which could lead to a denial of service.]
-
-|2019/04/11 |CVE-2019-10247 |Med |Med |< = 9.4.16 |9.2.28, 9.3.27, 9.4.17
-|https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-10247[If no webapp was mounted to the root namespace and a 404 was encountered, an HTML page would be generated displaying the fully qualified base resource location for each context.]
-
-|2019/04/11 |CVE-2019-10246 |High |High |< = 9.4.16 |9.2.28, 9.3.27, 9.4.17
-|https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-10246[Use of `DefaultServlet` or `ResourceHandler` with indexing was vulnerable to XSS behaviors to expose the directory listing on Windows operating systems.]
-
-|2019/04/11 |CVE-2019-10241 |High |High |< = 9.4.15 |9.2.27, 9.3.26, 9.4.16
-|https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-10241[Use of `DefaultServlet` or `ResourceHandler` with indexing was vulnerable to XSS behaviors to expose the directory listing.]
-
-|2018/06/25 |CVE-2018-12538 |High |High |>= 9.4.0, < = 9.4.8 |9.4.9
-|https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12538[`HttpSessions` present specifically in the FileSystem’s storage could be hijacked/accessed by an unauthorized user.]
-
-|2018/06/25 |CVE-2018-12536 |High |See https://cwe.mitre.org/data/definitions/209.html[CWE-202] |< = 9.4.10 |9.2.25, 9.3.24, 9.4.11
-|https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12536[`InvalidPathException` Message reveals webapp system path.]
-
-|2018/06/25 |CVE-2017-7658 |See https://cwe.mitre.org/data/definitions/444.html[CWE-444] |See https://cwe.mitre.org/data/definitions/444.html[CWE-444] |< = 9.4.10 |9.2.25, 9.3.24, 9.4.11
-|https://cve.mitre.org/cgi-bin/cvename.cgi?name=2017-7658[Too Tolerant Parser, Double Content-Length + Transfer-Encoding + Whitespace.]
-
-|2018/06/25 |CVE-2017-7657 |See https://cwe.mitre.org/data/definitions/444.html[CWE-444] |See https://cwe.mitre.org/data/definitions/444.html[CWE-444] |< = 9.4.10 |9.2.25, 9.3.24, 9.4.11
-|https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7657[HTTP/1.1 Request smuggling with carefully crafted body content (Does not apply to HTTP/1.0 or HTTP/2).]
-
-|2018/06/25 |CVE-2017-7656 |See https://cwe.mitre.org/data/definitions/444.html[CWE-444] |See https://cwe.mitre.org/data/definitions/444.html[CWE-444] |< = 9.4.10 |9.2.25, 9.3.24, 9.4.11
-|https://cve.mitre.org/cgi-bin/cvename.cgi?name=2017-7656[HTTP Request Smuggling when used with invalid request headers (for HTTP/0.9).]
-
-|2016/05/31 |CVE-2016-4800 |high |high |>= 9.3.0, < = 9.3.8 |9.3.9
-|http://www.ocert.org/advisories/ocert-2016-001.html[Alias vulnerability allowing access to protected resources within a webapp on Windows.]
-
-|2015/02/24 |http://blog.gdssecurity.com/labs/2015/2/25/jetleak-vulnerability-remote-leakage-of-shared-buffers-in-je.html[CVE-2015-2080] |high |high |>=9.2.3 <9.2.9 |9.2.9
-|JetLeak exposure of past buffers during HttpParser error
-
-|2013/11/27 |http://en.securitylab.ru/lab/PT-2013-65[PT-2013-65] |medium
-|high |>=9.0.0 <9.0.5 |9.0.6
-https://bugs.eclipse.org/bugs/show_bug.cgi?id=418014[418014] |Alias checking disabled by NTFS errors on Windows.
-
-|2013/07/24
-|https://bugs.eclipse.org/bugs/show_bug.cgi?id=413684[413684] |low
-|medium |>=7.6.9 <9.0.5 |7.6.13,8.1.13,9.0.5
-https://bugs.eclipse.org/bugs/show_bug.cgi?id=413684[413684]
-|Constraints bypassed if Unix symlink alias checker used on Windows.
-
-|2011/12/29
-|http://www.ocert.org/advisories/ocert-2011-003.html[CERT2011-003] http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2011-4461[CVE-2011-4461]
-|high |medium |All versions |7.6.0.RCO
-https://bugs.eclipse.org/bugs/show_bug.cgi?id=367638[Jetty-367638]
-|Added ContextHandler.setMaxFormKeys (intkeys) to limit the number of parameters (default 1000).
-
-|2009/11/05
-|http://www.kb.cert.org/vuls/id/120541[CERT2011-003] http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3555[CERT2011-003]
-|medium |high |JVM<1.6u19 |jetty-7.01.v20091125, jetty-6.1.22 |Work
-around by turning off SSL renegotiation in Jetty. If using JVM > 1.6u19
-setAllowRenegotiate(true) may be called on connectors.
-
-|2009/06/18 |Jetty-1042 |low
-|high |< = 6.1.18, < = 7.0.0.M4 |6.1.19, 7.0.0.Rc0 |Cookie leak between
-requests sharing a connection.
-
-|2009/04/30 |http://www.kb.cert.org/vuls/id/402580[CERT402580] |medium
-|high |< = 6.1.16, < = 7.0.0.M2 a|
-5.1.15, 6.1.18, 7.0.0.M2
-
-Jetty-1004
-
- |View arbitrary disk content in some specific configurations.
-
-|2007/12/22
-|http://www.kb.cert.org/vuls/id/553235[CERT553235] http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2007-6672[CVE-2007-6672]
-|high |medium |6.1.rrc0-6.1.6 a|
-6.1.7
-
-CERT553235
-
- |Static content visible in WEB-INF and past security constraints.
-
-|2007/11/05
-|http://www.kb.cert.org/vuls/id/438616[CERT438616] http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-5614[CVE-2007-5614]
-|low |low |<6.1.6 |6.1.6rc1 (patch in CVS for jetty5) |Single quote in
-cookie name.
-
-|2007/11/05
-|http://www.kb.cert.org/vuls/id/237888[CERT237888>] http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-5613[CVE-2007-5613]
-|low |low |<6.1.6 |6.1.6rc0 (patch in CVS for jetty5) |XSS in demo dup
-servlet.
-
-|2007/11/03 |http://www.kb.cert.org/vuls/id/212984[CERT212984
->] http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-5615[CVE-2007-5615]
-|medium |medium |<6.1.6 |6.1.6rc0 (patch in CVS for jetty5) |CRLF
-Response splitting.
-
-|2006/11/22
-|http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-6969[CVE-2006-6969]
-|low |high |<6.1.0, <6.0.2, <5.1.12, <4.2.27 |6.1.0pre3, 6.0.2, 5.1.12,
-4.2.27 |Session ID predictability.
-
-|2006/06/01
-|http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-2759[CVE-2006-2759]
-|medium |medium |<6.0.*, <6.0.0Beta17 |6.0.0Beta17 |JSP source
-visibility.
-
-|2006/01/05 | |medium |medium |<5.1.10 |5.1.10 |Fixed //security
-constraint bypass on Windows.
-
-|2005/11/18
-|http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-2758[CVE-2006-2758]
-|medium |medium |<5.1.6 |5.1.6, 6.0.0Beta4 |JSP source visibility.
-
-|2004/02/04 |JSSE 1.0.3_01 |medium |medium |<4.2.7 |4.2.7 |Upgraded JSSE
-to obtain downstream security fix.
-
-|2002/09/22 | |high |high |<4.1.0 |4.1.0 |Fixed CGI servlet remove
-exploit.
-
-|2002/03/12 | |medium | |<3.1.7 |4.0.RC2, 3.1.7 |Fixed // security
-constraint bypass.
-
-|2001/10/21 |medium | |high |<3.1.3 |3.1.3 |Fixed trailing null security
-constraint bypass.
-|=======================================================================
+We prefer that security issues are reported directly to Jetty developers as opposed through GitHub Issues since it currently has *no* facility to tag issues as _private_.
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java
index dae3883aaba..91d3e266893 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java
@@ -481,65 +481,60 @@ public class ForwardedRequestCustomizer implements Customizer
if (match)
{
- String proto = "http";
-
HttpURI.Mutable builder = HttpURI.build(request.getHttpURI());
boolean httpUriChanged = false;
// Is secure status configured from headers?
if (forwarded.isSecure())
{
- // set default to https
- proto = config.getSecureScheme();
+ request.setSecure(true);
}
// Set Scheme from configured protocol
if (forwarded._proto != null)
{
- proto = forwarded._proto;
- builder.scheme(proto);
+ builder.scheme(forwarded._proto);
+ httpUriChanged = true;
+ }
+ // Set scheme if header implies secure scheme is to be used (see #isSslIsSecure())
+ else if (forwarded._secureScheme)
+ {
+ builder.scheme(config.getSecureScheme());
httpUriChanged = true;
}
-
- // Set authority
- String host = null;
- int port = -1;
// Use authority from headers, if configured.
if (forwarded._authority != null)
{
- host = forwarded._authority._host;
- port = forwarded._authority._port;
+ String host = forwarded._authority._host;
+ int port = forwarded._authority._port;
+
+ // Fall back to request metadata if needed.
+ if (host == null)
+ {
+ host = builder.getHost();
+ }
+
+ if (port == MutableHostPort.UNSET) // is unset by headers
+ {
+ port = builder.getPort();
+ }
+
+ // Don't change port if port == IMPLIED.
+
+ // Update authority if different from metadata
+ if (!host.equalsIgnoreCase(builder.getHost()) ||
+ port != builder.getPort())
+ {
+ request.setHttpFields(HttpFields.build(httpFields, new HostPortHttpField(host, port)));
+ builder.authority(host, port);
+ httpUriChanged = true;
+ }
}
- // Fall back to request metadata if needed.
- if (host == null)
+ if (httpUriChanged)
{
- host = builder.getHost();
- }
- if (port == MutableHostPort.UNSET) // is unset by headers
- {
- port = builder.getPort();
- }
- // Don't change port if port == IMPLIED.
-
- // Update authority if different from metadata
- if (!host.equalsIgnoreCase(builder.getHost()) ||
- port != builder.getPort())
- {
- request.setHttpFields(HttpFields.build(httpFields, new HostPortHttpField(host, port)));
- builder.authority(host, port);
- httpUriChanged = true;
- }
-
- // Set secure status
- if (forwarded.isSecure() ||
- proto.equalsIgnoreCase(config.getSecureScheme()) ||
- port == getSecurePort(config))
- {
- request.setSecure(true);
- builder.scheme(proto);
- httpUriChanged = true;
+ request.setHttpURI(builder);
}
// Set Remote Address
@@ -548,11 +543,6 @@ public class ForwardedRequestCustomizer implements Customizer
int forPort = forwarded._for._port > 0 ? forwarded._for._port : request.getRemotePort();
request.setRemoteAddr(InetSocketAddress.createUnresolved(forwarded._for._host, forPort));
}
-
- if (httpUriChanged)
- {
- request.setHttpURI(builder);
- }
}
}
@@ -759,6 +749,7 @@ public class ForwardedRequestCustomizer implements Customizer
String _proto;
Source _protoSource = Source.UNSET;
Boolean _secure;
+ boolean _secureScheme = false;
public Forwarded(Request request, HttpConfiguration config)
{
@@ -802,40 +793,58 @@ public class ForwardedRequestCustomizer implements Customizer
return _for;
}
- @SuppressWarnings("unused")
+ /**
+ * Called if header is Proxy-auth-cert
+ */
public void handleCipherSuite(HttpField field)
{
_request.setAttribute("javax.servlet.request.cipher_suite", field.getValue());
+
+ // Is ForwardingRequestCustomizer configured to trigger isSecure and scheme change on this header?
if (isSslIsSecure())
{
_secure = true;
+ // track desire for secure scheme, actual protocol will be resolved later.
+ _secureScheme = true;
}
}
- @SuppressWarnings("unused")
+ /**
+ * Called if header is Proxy-Ssl-Id
+ */
public void handleSslSessionId(HttpField field)
{
_request.setAttribute("javax.servlet.request.ssl_session_id", field.getValue());
+
+ // Is ForwardingRequestCustomizer configured to trigger isSecure and scheme change on this header?
if (isSslIsSecure())
{
_secure = true;
+ // track desire for secure scheme, actual protocol will be resolved later.
+ _secureScheme = true;
}
}
- @SuppressWarnings("unused")
+ /**
+ * Called if header is X-Forwarded-Host
+ */
public void handleForwardedHost(HttpField field)
{
updateAuthority(getLeftMost(field.getValue()), Source.XFORWARDED_HOST);
}
- @SuppressWarnings("unused")
+ /**
+ * Called if header is X-Forwarded-For
+ */
public void handleForwardedFor(HttpField field)
{
HostPort hostField = new HostPort(getLeftMost(field.getValue()));
getFor().setHostPort(hostField, Source.XFORWARDED_FOR);
}
- @SuppressWarnings("unused")
+ /**
+ * Called if header is X-Forwarded-Server
+ */
public void handleForwardedServer(HttpField field)
{
if (getProxyAsAuthority())
@@ -843,7 +852,9 @@ public class ForwardedRequestCustomizer implements Customizer
updateAuthority(getLeftMost(field.getValue()), Source.XFORWARDED_SERVER);
}
- @SuppressWarnings("unused")
+ /**
+ * Called if header is X-Forwarded-Port
+ */
public void handleForwardedPort(HttpField field)
{
int port = HostPort.parsePort(getLeftMost(field.getValue()));
@@ -851,13 +862,17 @@ public class ForwardedRequestCustomizer implements Customizer
updatePort(port, Source.XFORWARDED_PORT);
}
- @SuppressWarnings("unused")
+ /**
+ * Called if header is X-Forwarded-Proto
+ */
public void handleProto(HttpField field)
{
updateProto(getLeftMost(field.getValue()), Source.XFORWARDED_PROTO);
}
- @SuppressWarnings("unused")
+ /**
+ * Called if header is X-Proxied-Https
+ */
public void handleHttps(HttpField field)
{
if ("on".equalsIgnoreCase(field.getValue()) || "true".equalsIgnoreCase(field.getValue()))
@@ -866,9 +881,21 @@ public class ForwardedRequestCustomizer implements Customizer
updateProto(HttpScheme.HTTPS.asString(), Source.XPROXIED_HTTPS);
updatePort(getSecurePort(_config), Source.XPROXIED_HTTPS);
}
+ else if ("off".equalsIgnoreCase(field.getValue()) || "false".equalsIgnoreCase(field.getValue()))
+ {
+ _secure = false;
+ updateProto(HttpScheme.HTTP.asString(), Source.XPROXIED_HTTPS);
+ updatePort(MutableHostPort.IMPLIED, Source.XPROXIED_HTTPS);
+ }
+ else
+ {
+ throw new BadMessageException("Invalid value for " + field.getName());
+ }
}
- @SuppressWarnings("unused")
+ /**
+ * Called if header is Forwarded
+ */
public void handleRFC7239(HttpField field)
{
addValue(field.getValue());
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
index cf8e24d5516..5414f93c6a6 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
@@ -38,6 +38,7 @@ import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
@@ -693,6 +694,8 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
_request.setMetaData(request);
+ _request.setSecure(HttpScheme.HTTPS.is(request.getURI().getScheme()));
+
_combinedListener.onRequestBegin(_request);
if (LOG.isDebugEnabled())
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java
index 1d07a6baf6f..2c82a4f081c 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java
@@ -32,7 +32,6 @@ import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
@@ -135,6 +134,51 @@ public class ForwardedRequestCustomizerTest
public static Stream cases()
{
return Stream.of(
+ // HTTP 1.0
+ Arguments.of(
+ new Request("HTTP/1.0 - no Host header")
+ .headers(
+ "GET /example HTTP/1.0"
+ ),
+ new Expectations()
+ .scheme("http").serverName("0.0.0.0").serverPort(80)
+ .secure(false)
+ .requestURL("http://0.0.0.0/example")
+ ),
+ Arguments.of(
+ new Request("HTTP/1.0 - Empty Host header")
+ .headers(
+ "GET /example HTTP/1.0",
+ "Host:"
+ ),
+ new Expectations()
+ .scheme("http").serverName("0.0.0.0").serverPort(80)
+ .secure(false)
+ .requestURL("http://0.0.0.0/example")
+ ),
+ Arguments.of(
+ new Request("HTTP/1.0 - No Host header, with X-Forwarded-Host")
+ .headers(
+ "GET /example HTTP/1.0",
+ "X-Forwarded-Host: alt.example.net:7070"
+ ),
+ new Expectations()
+ .scheme("http").serverName("alt.example.net").serverPort(7070)
+ .secure(false)
+ .requestURL("http://alt.example.net:7070/example")
+ ),
+ Arguments.of(
+ new Request("HTTP/1.0 - Empty Host header, with X-Forwarded-Host")
+ .headers(
+ "GET /example HTTP/1.0",
+ "Host:",
+ "X-Forwarded-Host: alt.example.net:7070"
+ ),
+ new Expectations()
+ .scheme("http").serverName("alt.example.net").serverPort(7070)
+ .secure(false)
+ .requestURL("http://alt.example.net:7070/example")
+ ),
// Host IPv4
Arguments.of(
new Request("IPv4 Host Only")
@@ -144,6 +188,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("1.2.3.4").serverPort(2222)
+ .secure(false)
.requestURL("http://1.2.3.4:2222/")
),
Arguments.of(new Request("IPv6 Host Only")
@@ -153,16 +198,18 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("[::1]").serverPort(2222)
+ .secure(false)
.requestURL("http://[::1]:2222/")
),
Arguments.of(new Request("IPv4 in Request Line")
.headers(
- "GET http://1.2.3.4:2222/ HTTP/1.1",
+ "GET https://1.2.3.4:2222/ HTTP/1.1",
"Host: wrong"
),
new Expectations()
- .scheme("http").serverName("1.2.3.4").serverPort(2222)
- .requestURL("http://1.2.3.4:2222/")
+ .scheme("https").serverName("1.2.3.4").serverPort(2222)
+ .secure(true)
+ .requestURL("https://1.2.3.4:2222/")
),
Arguments.of(new Request("IPv6 in Request Line")
.headers(
@@ -171,6 +218,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("[::1]").serverPort(2222)
+ .secure(false)
.requestURL("http://[::1]:2222/")
),
@@ -187,6 +235,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(80)
+ .secure(false)
.requestURL("http://myhost/")
.remoteAddr("[2001:db8:cafe::17]").remotePort(4711)
),
@@ -200,6 +249,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(80)
+ .secure(false)
.requestURL("http://myhost/")
.remoteAddr("192.0.2.43").remotePort(0)
),
@@ -213,6 +263,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(80)
+ .secure(false)
.requestURL("http://myhost/")
.remoteAddr("192.0.2.43").remotePort(0)
),
@@ -227,6 +278,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(80)
+ .secure(false)
.requestURL("http://myhost/")
.remoteAddr("192.0.2.43").remotePort(0)
),
@@ -240,6 +292,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(80)
+ .secure(false)
.requestURL("http://myhost/")
.remoteAddr("192.0.2.43").remotePort(0)
),
@@ -251,6 +304,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(80)
+ .secure(false)
.requestURL("http://myhost/")
.remoteAddr("192.0.2.43").remotePort(0)
),
@@ -264,6 +318,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("example.com").serverPort(80)
+ .secure(false)
.requestURL("http://example.com/")
.remoteAddr("192.0.2.43").remotePort(0)
),
@@ -277,6 +332,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("myhost").serverPort(443)
+ .secure(true)
.requestURL("https://myhost/")
),
@@ -291,6 +347,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("example.com").serverPort(80)
+ .secure(false)
.remoteAddr("10.20.30.40")
.requestURL("http://example.com/")
),
@@ -305,6 +362,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("example.com").serverPort(81)
+ .secure(true)
.remoteAddr("10.20.30.40")
.requestURL("https://example.com:81/")
),
@@ -317,6 +375,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("example.com").serverPort(443)
+ .secure(true)
.requestURL("https://example.com/")
),
Arguments.of(new Request("ProxyPass (IPv6 from [::1]:80 to localhost:8080)")
@@ -328,6 +387,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("[::1]").serverPort(80)
+ .secure(false)
.remoteAddr("10.20.30.40")
.requestURL("http://[::1]/")
),
@@ -340,6 +400,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("[::1]").serverPort(8888)
+ .secure(false)
.remoteAddr("10.20.30.40")
.requestURL("http://[::1]:8888/")
),
@@ -354,6 +415,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("example.com").serverPort(443)
+ .secure(true)
.remoteAddr("10.20.30.40")
.requestURL("https://example.com/")
),
@@ -367,6 +429,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("myhost").serverPort(443)
+ .secure(true)
.requestURL("https://myhost/")
),
Arguments.of(new Request("X-Forwarded-For (multiple headers)")
@@ -378,6 +441,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(80)
+ .secure(false)
.requestURL("http://myhost/")
.remoteAddr("10.9.8.7").remotePort(0)
),
@@ -389,6 +453,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(80)
+ .secure(false)
.requestURL("http://myhost/")
.remoteAddr("10.9.8.7").remotePort(1111)
),
@@ -400,6 +465,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(80)
+ .secure(false)
.requestURL("http://myhost/")
.remoteAddr("[2001:db8:cafe::17]").remotePort(1111)
),
@@ -412,6 +478,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(2222)
+ .secure(false)
.requestURL("http://myhost:2222/")
.remoteAddr("[1:2:3:4:5:6:7:8]").remotePort(0)
),
@@ -426,6 +493,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(2222)
+ .secure(false)
.requestURL("http://myhost:2222/")
.remoteAddr("[1:2:3:4:5:6:7:8]").remotePort(0)
),
@@ -438,6 +506,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(2222)
+ .secure(false)
.requestURL("http://myhost:2222/")
.remoteAddr("[1:2:3:4:5:6:7:8]").remotePort(0)
),
@@ -450,6 +519,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(4444)
+ .secure(false)
.requestURL("http://myhost:4444/")
.remoteAddr("192.168.1.200").remotePort(0)
),
@@ -463,6 +533,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(80)
+ .secure(false)
.requestURL("http://myhost/")
.remoteAddr("192.168.1.200").remotePort(4444)
),
@@ -475,6 +546,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(4444)
+ .secure(false)
.requestURL("http://myhost:4444/")
.remoteAddr("192.168.1.200").remotePort(0)
),
@@ -489,6 +561,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("www.example.com").serverPort(4333)
+ .secure(true)
.requestURL("https://www.example.com:4333/")
.remoteAddr("8.5.4.3").remotePort(2222)
),
@@ -503,6 +576,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("www.example.com").serverPort(4333)
+ .secure(true)
.requestURL("https://www.example.com:4333/")
.remoteAddr("8.5.4.3").remotePort(2222)
),
@@ -518,6 +592,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("www.example.com").serverPort(4333)
+ .secure(true)
.requestURL("https://www.example.com:4333/")
.remoteAddr("8.5.4.3").remotePort(2222)
),
@@ -533,6 +608,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("www.example.com").serverPort(4333)
+ .secure(true)
.requestURL("https://www.example.com:4333/")
.remoteAddr("8.5.4.3").remotePort(2222)
),
@@ -548,6 +624,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("www.example.com").serverPort(4333)
+ .secure(true)
.requestURL("https://www.example.com:4333/")
.remoteAddr("8.5.4.3").remotePort(2222)
),
@@ -561,6 +638,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("fw.example.com").serverPort(4333)
+ .secure(false)
.requestURL("http://fw.example.com:4333/")
.remoteAddr("8.5.4.3").remotePort(2222)
),
@@ -574,6 +652,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("fw.example.com").serverPort(4333)
+ .secure(false)
.requestURL("http://fw.example.com:4333/")
.remoteAddr("8.5.4.3").remotePort(2222)
),
@@ -589,6 +668,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("sub1.example.com").serverPort(10003)
+ .secure(true)
.requestURL("https://sub1.example.com:10003/")
.remoteAddr("127.0.0.1").remotePort(8888)
),
@@ -604,6 +684,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("sub1.example.com").serverPort(10003)
+ .secure(true)
.requestURL("https://sub1.example.com:10003/")
.remoteAddr("127.0.0.1").remotePort(8888)
),
@@ -620,6 +701,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("sub1.example.com").serverPort(10003)
+ .secure(true)
.requestURL("https://sub1.example.com:10003/")
.remoteAddr("127.0.0.1").remotePort(8888)
),
@@ -635,6 +717,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("sub1.example.com").serverPort(10003)
+ .secure(true)
.requestURL("https://sub1.example.com:10003/")
.remoteAddr("127.0.0.1").remotePort(8888)
),
@@ -651,9 +734,35 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("example.com").serverPort(80)
+ .secure(false)
.requestURL("http://example.com/")
.remoteAddr("192.0.2.43").remotePort(0)
),
+ Arguments.of(
+ new Request("RFC7239 - mixed with HTTP/1.0 - No Host header")
+ .headers(
+ "GET /example HTTP/1.0",
+ "Forwarded: for=1.1.1.1:6060,proto=http;host=alt.example.net:7070"
+ ),
+ new Expectations()
+ .scheme("http").serverName("alt.example.net").serverPort(7070)
+ .secure(false)
+ .requestURL("http://alt.example.net:7070/example")
+ .remoteAddr("1.1.1.1").remotePort(6060)
+ ),
+ Arguments.of(
+ new Request("RFC7239 - mixed with HTTP/1.0 - Empty Host header")
+ .headers(
+ "GET /example HTTP/1.0",
+ "Host:",
+ "Forwarded: for=1.1.1.1:6060,proto=http;host=alt.example.net:7070"
+ ),
+ new Expectations()
+ .scheme("http").serverName("alt.example.net").serverPort(7070)
+ .secure(false)
+ .requestURL("http://alt.example.net:7070/example")
+ .remoteAddr("1.1.1.1").remotePort(6060)
+ ),
// =================================================================
// Forced Behavior
Arguments.of(new Request("Forced Host (no port)")
@@ -666,6 +775,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("always.example.com").serverPort(80)
+ .secure(false)
.requestURL("http://always.example.com/")
.remoteAddr("11.9.8.7").remotePort(1111)
),
@@ -679,6 +789,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("always.example.com").serverPort(9090)
+ .secure(false)
.requestURL("http://always.example.com:9090/")
.remoteAddr("11.9.8.7").remotePort(1111)
),
@@ -692,6 +803,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("myhost").serverPort(443)
+ .secure(true)
.requestURL("https://myhost/")
.remoteAddr("0.0.0.0").remotePort(0)
),
@@ -704,6 +816,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(80)
+ .secure(false)
.requestURL("http://myhost/")
.remoteAddr("0.0.0.0").remotePort(0)
.sslSession("Wibble")
@@ -717,6 +830,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("myhost").serverPort(443)
+ .secure(true)
.requestURL("https://myhost/")
.remoteAddr("0.0.0.0").remotePort(0)
.sslSession("0123456789abcdef")
@@ -730,6 +844,7 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("http").serverName("myhost").serverPort(80)
+ .secure(false)
.requestURL("http://myhost/")
.remoteAddr("0.0.0.0").remotePort(0)
.sslCertificate("Wibble")
@@ -743,9 +858,121 @@ public class ForwardedRequestCustomizerTest
),
new Expectations()
.scheme("https").serverName("myhost").serverPort(443)
+ .secure(true)
.requestURL("https://myhost/")
.remoteAddr("0.0.0.0").remotePort(0)
.sslCertificate("0123456789abcdef")
+ ),
+ // =================================================================
+ // Complicated scenarios
+ Arguments.of(new Request("No initial authority, X-Forwarded-Proto on http, Proxy-Ssl-Id exists (setSslIsSecure==true)")
+ .configureCustomizer((customizer) -> customizer.setSslIsSecure(true))
+ .headers(
+ "GET /foo HTTP/1.1",
+ "Host: myhost",
+ "X-Forwarded-Proto: http",
+ "Proxy-Ssl-Id: Wibble"
+ ),
+ new Expectations()
+ .scheme("http").serverName("myhost").serverPort(80)
+ .secure(true)
+ .requestURL("http://myhost/foo")
+ .remoteAddr("0.0.0.0").remotePort(0)
+ .sslSession("Wibble")
+ ),
+ Arguments.of(new Request("https initial authority, X-Forwarded-Proto on http, Proxy-Ssl-Id exists (setSslIsSecure==false)")
+ .configureCustomizer((customizer) -> customizer.setSslIsSecure(false))
+ .headers(
+ "GET https://alt.example.net/foo HTTP/1.1",
+ "Host: myhost",
+ "X-Forwarded-Proto: http",
+ "Proxy-Ssl-Id: Wibble"
+ ),
+ new Expectations()
+ .scheme("http").serverName("alt.example.net").serverPort(80)
+ .secure(false)
+ .requestURL("http://alt.example.net/foo")
+ .remoteAddr("0.0.0.0").remotePort(0)
+ .sslSession("Wibble")
+ ),
+ Arguments.of(new Request("No initial authority, X-Proxied-Https off, Proxy-Ssl-Id exists (setSslIsSecure==true)")
+ .configureCustomizer((customizer) -> customizer.setSslIsSecure(true))
+ .headers(
+ "GET /foo HTTP/1.1",
+ "Host: myhost",
+ "X-Proxied-Https: off", // this wins for scheme and secure
+ "Proxy-Ssl-Id: Wibble"
+ ),
+ new Expectations()
+ .scheme("http").serverName("myhost").serverPort(80)
+ .secure(false)
+ .requestURL("http://myhost/foo")
+ .remoteAddr("0.0.0.0").remotePort(0)
+ .sslSession("Wibble")
+ ),
+ Arguments.of(new Request("Https initial authority, X-Proxied-Https off, Proxy-Ssl-Id exists (setSslIsSecure==true)")
+ .configureCustomizer((customizer) -> customizer.setSslIsSecure(true))
+ .headers(
+ "GET https://alt.example.net/foo HTTP/1.1",
+ "Host: myhost",
+ "X-Proxied-Https: off", // this wins for scheme and secure
+ "Proxy-Ssl-Id: Wibble"
+ ),
+ new Expectations()
+ .scheme("http").serverName("alt.example.net").serverPort(80)
+ .secure(false)
+ .requestURL("http://alt.example.net/foo")
+ .remoteAddr("0.0.0.0").remotePort(0)
+ .sslSession("Wibble")
+ ),
+ Arguments.of(new Request("Https initial authority, X-Proxied-Https off, Proxy-Ssl-Id exists (setSslIsSecure==true) (alt order)")
+ .configureCustomizer((customizer) -> customizer.setSslIsSecure(true))
+ .headers(
+ "GET https://alt.example.net/foo HTTP/1.1",
+ "Host: myhost",
+ "Proxy-Ssl-Id: Wibble",
+ "X-Proxied-Https: off" // this wins for scheme and secure
+ ),
+ new Expectations()
+ .scheme("http").serverName("alt.example.net").serverPort(80)
+ .secure(false)
+ .requestURL("http://alt.example.net/foo")
+ .remoteAddr("0.0.0.0").remotePort(0)
+ .sslSession("Wibble")
+ ),
+ Arguments.of(new Request("Http initial authority, X-Proxied-Https off, Proxy-Ssl-Id exists (setSslIsSecure==false)")
+ .configureCustomizer((customizer) -> customizer.setSslIsSecure(false))
+ .headers(
+ "GET https://alt.example.net/foo HTTP/1.1",
+ "Host: myhost",
+ "X-Proxied-Https: off",
+ "Proxy-Ssl-Id: Wibble",
+ "Proxy-auth-cert: 0123456789abcdef"
+ ),
+ new Expectations()
+ .scheme("http").serverName("alt.example.net").serverPort(80)
+ .secure(false)
+ .requestURL("http://alt.example.net/foo")
+ .remoteAddr("0.0.0.0").remotePort(0)
+ .sslSession("Wibble")
+ .sslCertificate("0123456789abcdef")
+ ),
+ Arguments.of(new Request("Http initial authority, X-Proxied-Https off, Proxy-Ssl-Id exists (setSslIsSecure==false) (alt)")
+ .configureCustomizer((customizer) -> customizer.setSslIsSecure(false))
+ .headers(
+ "GET https://alt.example.net/foo HTTP/1.1",
+ "Host: myhost",
+ "Proxy-Ssl-Id: Wibble",
+ "Proxy-auth-cert: 0123456789abcdef",
+ "X-Proxied-Https: off"
+ ),
+ new Expectations()
+ .scheme("http").serverName("alt.example.net").serverPort(80)
+ .secure(false)
+ .requestURL("http://alt.example.net/foo")
+ .remoteAddr("0.0.0.0").remotePort(0)
+ .sslSession("Wibble")
+ .sslCertificate("0123456789abcdef")
)
);
}
@@ -848,20 +1075,31 @@ public class ForwardedRequestCustomizerTest
expectations.accept(actual);
}
- @Test
- public void testBadInput() throws Exception
+ public static Stream badRequestCases()
{
- Request request = new Request("Bad port value")
- .headers(
- "GET / HTTP/1.1",
- "Host: myhost",
- "X-Forwarded-Port: "
- );
+ return Stream.of(
+ new Request("Bad port value")
+ .headers(
+ "GET / HTTP/1.1",
+ "Host: myhost",
+ "X-Forwarded-Port: "
+ ),
+ new Request("Invalid X-Proxied-Https value")
+ .headers(
+ "GET / HTTP/1.1",
+ "Host: myhost",
+ "X-Proxied-Https: foo"
+ )
+ );
+ }
+ @ParameterizedTest
+ @MethodSource("badRequestCases")
+ public void testBadInput(Request request) throws Exception
+ {
request.configure(customizer);
String rawRequest = request.getRawRequest((header) -> header);
- // System.out.println(rawRequest);
HttpTester.Response response = HttpTester.parseResponse(connector.getResponse(rawRequest));
assertThat("status", response.getStatus(), is(400));
@@ -926,12 +1164,13 @@ public class ForwardedRequestCustomizerTest
int expectedRemotePort = 0;
String expectedSslSession;
String expectedSslCertificate;
+ Boolean secure;
@Override
public void accept(Actual actual)
{
assertThat("scheme", actual.scheme.get(), is(expectedScheme));
- if (actual.scheme.get().equals("https"))
+ if (secure != null && secure)
{
assertTrue(actual.wasSecure.get(), "wasSecure");
}
@@ -953,6 +1192,12 @@ public class ForwardedRequestCustomizerTest
}
}
+ public Expectations secure(boolean flag)
+ {
+ this.secure = flag;
+ return this;
+ }
+
public Expectations scheme(String scheme)
{
this.expectedScheme = scheme;