package divconq.hub; import io.netty.handler.codec.http.HttpResponse; import javax.net.ssl.SSLEngine; import org.joda.time.DateTime; import org.joda.time.Period; import org.joda.time.format.ISOPeriodFormat; import divconq.lang.op.OperationContext; import divconq.log.Logger; import divconq.struct.Struct; import divconq.util.StringUtil; import divconq.xml.XAttribute; import divconq.xml.XElement; public class SecurityPolicy { public void hardenHttpResponse(HttpResponse resp) { OperationContext ctx = OperationContext.get(); String did = ctx.getUserContext().getDomainId(); if (StringUtil.isNotEmpty(did)) { DomainInfo domain = Hub.instance.getDomainInfo(did); XElement config = domain.getSettings(); XElement http = null; if (config != null) http = config.selectFirst("Harden/Http"); this.hardenHttpResponseConfig(resp, http, false); } HubResources resources = Hub.instance.getResources(); // impossible, so something is wrong with Hub just skip this if (resources == null) return; XElement http = resources.getConfig().selectFirst("Harden/Http"); this.hardenHttpResponseConfig(resp, http, true); } public void hardenHttpResponseConfig(HttpResponse resp, XElement http, boolean root) { if (http == null) return; // is Http is Strict then just set a standard template if ("Strict".equals(http.getAttribute("Mode"))) http = new XElement("Http", new XAttribute("Mode", "Strict"), new XElement("ContentSecurityPolicy", new XAttribute("Mode", "Strict")), new XElement("Hsts", new XAttribute("Mode", "Strict")), new XElement("Header", new XAttribute("Name", "X-Content-Type-Options"), new XAttribute("Value", "nosniff")), new XElement("Header", new XAttribute("Name", "X-XSS-Protection"), new XAttribute("Value", "1;mode=block")), new XElement("Header", new XAttribute("Name", "X-Frame-Options"), new XAttribute("Value", "deny")) ); XElement hsts = http.find("Hsts"); // custom STS can use Header tags instead if (hsts != null) { boolean hstsForce = Struct.objectToBoolean(hsts.getAttribute("Override", "False")); String mode = hsts.getAttribute("Mode", "Strict"); if (!resp.headers().contains("Strict-Transport-Security") || hstsForce) { String age = hsts.getAttribute("Age", "P5Y"); try { Period period = ISOPeriodFormat.standard().parsePeriod(age); long mage = new DateTime().plus(period).getMillis() / 1000; if ("Strict".equals(mode)) resp.headers().set("Strict-Transport-Security", "max-age=" + mage + "; includeSubDomains"); else if ("Self".equals(mode)) resp.headers().set("Strict-Transport-Security", "max-age=" + mage + ";"); } catch (Exception x) { OperationContext.get().error("Bad age value for Strict-Transport-Security"); } } } XElement csp = http.find("ContentSecurityPolicy"); // if root and no config, provide a default config // custom CSP can use Header tags instead if (csp != null) { boolean cspForce = Struct.objectToBoolean(csp.getAttribute("Override", "False")); boolean cspReport = Struct.objectToBoolean(csp.getAttribute("ReportOnly", "False")); String mode = csp.getAttribute("Mode", "Strict"); String name = "Content-Security-Policy"; if (cspReport) name = "-Report-Only"; if (!resp.headers().contains(name) || cspForce) { if ("Strict".equals(mode)) { resp.headers().set(name, "default-src 'self'; img-src 'self' data:; media-src mediastream:; frame-ancestors 'self'; connect-src *;"); } else if ("Loose".equals(mode)) { resp.headers().set(name, "default-src 'self'; img-src *; media-src *; font-src *; style-src 'unsafe-inline' *; frame-ancestors 'self'; connect-src *;"); } } } for (XElement hdr : http.selectAll("Header")) { boolean force = Struct.objectToBoolean(hdr.getAttribute("Override", "False")); String name = hdr.getAttribute("Name"); String value = hdr.getAttribute("Value"); if (StringUtil.isEmpty(name)) continue; if (!resp.headers().contains(name) || force) { if (StringUtil.isEmpty(value)) resp.headers().remove(name); else resp.headers().set(name, value); } } } public void hardenPublic(SSLEngine engine) { this.harden(engine); } public void hardenBus(SSLEngine engine) { this.harden(engine); } public void harden(SSLEngine engine) { HubResources resources = Hub.instance.getResources(); // impossible, so something is wrong with Hub just skip this if (resources == null) return; // default not using SSLv2Hello, SSLv3, TLSv1, TLSv1.1 - see issue #22 // also use only top of the line ciphers //engine.setEnabledProtocols(new String[] { "SSLv2Hello", "TLSv1", "TLSv1.1", "TLSv1.2" }); XElement tls = resources.getConfig().selectFirst("Harden/TLS"); if ((tls == null) || "Strict".equals(tls.getAttribute("Mode", "Strict"))) { engine.setEnabledProtocols(new String[] { "TLSv1.2" }); engine.setEnabledCipherSuites(new String[] { // AES 256 GCM SHA 384 "TLS_RSA_WITH_AES_256_GCM_SHA384", //-- "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", //-- "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", // AES 256 CBC SHA 384 "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", // AES 256 CBC SHA 256 "TLS_RSA_WITH_AES_256_CBC_SHA256", //-- "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", //-- "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", // AES 128 GCM SHA 256 "TLS_RSA_WITH_AES_128_GCM_SHA256", //-- "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", //-- "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" // SCSV //++ "TLS_FALLBACK_SCSV" //-- "TLS_EMPTY_RENEGOTIATION_INFO_SCSV" }); } else if ("Loose".equals(tls.getAttribute("Mode"))) { engine.setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" }); engine.setEnabledCipherSuites(new String[] { // AES 256 GCM SHA 384 "TLS_RSA_WITH_AES_256_GCM_SHA384", //-- "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", //-- "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", // AES 256 CBC SHA 384 "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", // AES 256 CBC SHA 256 "TLS_RSA_WITH_AES_256_CBC_SHA256", //-- "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", //-- "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", // AES 128 GCM SHA 256 "TLS_RSA_WITH_AES_128_GCM_SHA256", //-- "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", //-- "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", // AES 128 CBC SHA 256 "TLS_RSA_WITH_AES_128_CBC_SHA256", //-- "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", //-- "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", // AES 128 CBC SHA 128 "TLS_RSA_WITH_AES_128_CBC_SHA", //-- "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", //-- "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" // SCSV //++ "TLS_FALLBACK_SCSV" //-- "TLS_EMPTY_RENEGOTIATION_INFO_SCSV" // RC4 128 SHA1 //-- "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", //-- "TLS_ECDHE_RSA_WITH_RC4_128_SHA", //-- "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", //-- "TLS_ECDH_RSA_WITH_RC4_128_SHA" }); } // custom else { engine.setEnabledProtocols(tls.getAttribute("Protocols", "").split(",")); engine.setEnabledCipherSuites(tls.getAttribute("Suites", "").split(",")); } if (engine.getEnabledProtocols().length == 0) Logger.warn("No Protocols are enabled!!"); if (engine.getEnabledCipherSuites().length == 0) Logger.warn("No Cipher are enabled!!"); /* if (Logger.isTrace()) { System.out.println("Enabled"); for (String p : engine.getEnabledProtocols()) System.out.println("Proto: " + p); for (String p : engine.getEnabledCipherSuites()) System.out.println("Suite: " + p); System.out.println(); System.out.println("Supported"); System.out.println(); for (String p : engine.getSupportedProtocols()) System.out.println("Proto: " + p); for (String p : engine.getSupportedCipherSuites()) System.out.println("Suite: " + p); } */ } }