Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-10.0.x

This commit is contained in:
Joakim Erdfelt 2020-10-13 12:36:01 -05:00
commit 68560090fe
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
5 changed files with 353 additions and 236 deletions

View File

@ -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_.

View File

@ -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 FileSystems 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_.

View File

@ -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 <code>Proxy-auth-cert</code>
*/
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 <code>Proxy-Ssl-Id</code>
*/
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 <code>X-Forwarded-Host</code>
*/
public void handleForwardedHost(HttpField field)
{
updateAuthority(getLeftMost(field.getValue()), Source.XFORWARDED_HOST);
}
@SuppressWarnings("unused")
/**
* Called if header is <code>X-Forwarded-For</code>
*/
public void handleForwardedFor(HttpField field)
{
HostPort hostField = new HostPort(getLeftMost(field.getValue()));
getFor().setHostPort(hostField, Source.XFORWARDED_FOR);
}
@SuppressWarnings("unused")
/**
* Called if header is <code>X-Forwarded-Server</code>
*/
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 <code>X-Forwarded-Port</code>
*/
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 <code>X-Forwarded-Proto</code>
*/
public void handleProto(HttpField field)
{
updateProto(getLeftMost(field.getValue()), Source.XFORWARDED_PROTO);
}
@SuppressWarnings("unused")
/**
* Called if header is <code>X-Proxied-Https</code>
*/
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 <code>Forwarded</code>
*/
public void handleRFC7239(HttpField field)
{
addValue(field.getValue());

View File

@ -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())

View File

@ -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<Arguments> 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<Request> 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;