From 19d6e36ab961b9b8db97c66ebde973f36e37f959 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 23 Dec 2015 17:15:24 +1100 Subject: [PATCH] 484657 - Support HSTS rfc6797 --- .../jetty/embedded/ManyConnectors.java | 5 +- .../org/eclipse/jetty/http/HttpHeader.java | 4 +- .../jetty/server/SecureRequestCustomizer.java | 121 +++++++++++++++++- 3 files changed, 124 insertions(+), 6 deletions(-) diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java index 34a12c09cb2..53758ef75c8 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java @@ -97,7 +97,10 @@ public class ManyConnectors // resolve the https connection before handing control over to the Jetty // Server. HttpConfiguration https_config = new HttpConfiguration(http_config); - https_config.addCustomizer(new SecureRequestCustomizer()); + SecureRequestCustomizer src = new SecureRequestCustomizer(); + src.setStsMaxAge(2000); + src.setStsIncludeSubDomains(true); + https_config.addCustomizer(src); // HTTPS connector // We create a second ServerConnector, passing in the http configuration diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java index 54735cb57ae..0772da46826 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java @@ -112,6 +112,8 @@ public enum HttpHeader X_POWERED_BY("X-Powered-By"), HTTP2_SETTINGS("HTTP2-Settings"), + STRICT_TRANSPORT_SECURITY("Strict-Transport-Security"), + /* ------------------------------------------------------------ */ /** HTTP2 Fields. */ @@ -125,7 +127,7 @@ public enum HttpHeader /* ------------------------------------------------------------ */ - public final static Trie CACHE= new ArrayTrie<>(530); + public final static Trie CACHE= new ArrayTrie<>(560); static { for (HttpHeader header : HttpHeader.values()) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java index 7dc74588d80..c7409aaf938 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SecureRequestCustomizer.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.server; import java.security.cert.X509Certificate; +import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; @@ -26,10 +27,14 @@ import javax.net.ssl.SSLSession; import javax.servlet.ServletRequest; import org.eclipse.jetty.http.BadMessageException; +import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint; import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.ssl.SniX509ExtendedKeyManager; @@ -51,16 +56,104 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer public static final String CACHED_INFO_ATTR = CachedInfo.class.getName(); private boolean _sniHostCheck; - + private long _stsMaxAge=-1; + private boolean _stsIncludeSubDomains; + private HttpField _stsField; public SecureRequestCustomizer() { this(true); } - public SecureRequestCustomizer(boolean sniHostCheck) + public SecureRequestCustomizer(@Name("sniHostCheck")boolean sniHostCheck) + { + this(sniHostCheck,-1,false); + } + + /** + * @param sniHostCheck True if the SNI Host name must match. + * @param stsMaxAgeSeconds The max age in seconds for a Strict-Transport-Security response header. If set less than zero then no header is sent. + * @param stsIncludeSubdomains If true, a include subdomain property is sent with any Strict-Transport-Security header + */ + public SecureRequestCustomizer( + @Name("sniHostCheck")boolean sniHostCheck, + @Name("stsMaxAgeSeconds")long stsMaxAgeSeconds, + @Name("stsIncludeSubdomains")boolean stsIncludeSubdomains) { _sniHostCheck=sniHostCheck; + _stsMaxAge=stsMaxAgeSeconds; + _stsIncludeSubDomains=stsIncludeSubdomains; + formatSTS(); + } + + /** + * @return True if the SNI Host name must match. + */ + public boolean isSniHostCheck() + { + return _sniHostCheck; + } + + /** + * @param sniHostCheck True if the SNI Host name must match. + */ + public void setSniHostCheck(boolean sniHostCheck) + { + _sniHostCheck = sniHostCheck; + } + + /** + * @return The max age in seconds for a Strict-Transport-Security response header. If set less than zero then no header is sent. + */ + public long getStsMaxAge() + { + return _stsMaxAge; + } + + /** + * Set the Strict-Transport-Security max age. + * @param stsMaxAgeSeconds The max age in seconds for a Strict-Transport-Security response header. If set less than zero then no header is sent. + */ + public void setStsMaxAge(long stsMaxAgeSeconds) + { + _stsMaxAge = stsMaxAgeSeconds; + formatSTS(); + } + + /** + * Convenience method to call {@link #setStsMaxAge(long)} + * @param period The period in units + * @param units The {@link TimeUnit} of the period + */ + public void setStsMaxAge(long period,TimeUnit units) + { + _stsMaxAge = units.toSeconds(period); + formatSTS(); + } + + /** + * @return true if a include subdomain property is sent with any Strict-Transport-Security header + */ + public boolean isStsIncludeSubDomains() + { + return _stsIncludeSubDomains; + } + + /** + * @param stsIncludeSubDomains If true, a include subdomain property is sent with any Strict-Transport-Security header + */ + public void setStsIncludeSubDomains(boolean stsIncludeSubDomains) + { + _stsIncludeSubDomains = stsIncludeSubDomains; + formatSTS(); + } + + private void formatSTS() + { + if (_stsMaxAge<0) + _stsField=null; + else + _stsField=new PreEncodedHttpField(HttpHeader.STRICT_TRANSPORT_SECURITY,String.format("max-age=%d%s",_stsMaxAge,_stsIncludeSubDomains?"; includeSubDomains":"")); } @Override @@ -68,7 +161,6 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer { if (request.getHttpChannel().getEndPoint() instanceof DecryptedEndPoint) { - request.setSecure(true); if (request.getHttpURI().getScheme()==null) request.setScheme(HttpScheme.HTTPS.asString()); @@ -78,8 +170,29 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer SSLEngine sslEngine=sslConnection.getSSLEngine(); customize(sslEngine,request); } + + if (HttpScheme.HTTPS.is(request.getScheme())) + customizeSecure(request); } + + /** + *

+ * Customizes the request attributes for general secure settings. + * The default impl calls {@link Request#setSecure(boolean)} with true + * and sets a response header if the Strict-Transport-Security options + * are set. + *

+ */ + protected void customizeSecure(Request request) + { + request.setSecure(true); + + if (_stsField!=null) + request.getResponse().getHttpFields().add(_stsField); + } + + /** *

* Customizes the request attributes to be set for SSL requests. @@ -102,7 +215,7 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer * @param request * HttpRequest to be customized. */ - public void customize(SSLEngine sslEngine, Request request) + protected void customize(SSLEngine sslEngine, Request request) { request.setScheme(HttpScheme.HTTPS.asString()); SSLSession sslSession = sslEngine.getSession();