Hello,
As an occasional user of Tomcat I was missing HTTP Public Key Pinning
header support¹. So I have added it to the existing
"HttpHeaderSecurityFilter" class and would like to share it with you in
case you are interested. Please see the attached patch.
Best Regards,
Patrick Beckmann
¹ https://tools.ietf.org/html/rfc7469
Index: conf/web.xml
===================================================================
--- conf/web.xml (revision 1734844)
+++ conf/web.xml (working copy)
@@ -436,6 +436,36 @@
<!-- Should the includeSubDomains parameter be -->
<!-- included in the HSTS header. -->
<!-- -->
+ <!-- hpkpEnabled Should the HTTP Public Key Pinning (HPKP) -->
+ <!-- header be added to the response? See RFC 7469 -->
+ <!-- for more information on HPKP. [false] -->
+ <!-- -->
+ <!-- hpkpReportOnly Should a HPKP "report only" header be set -->
+ <!-- instead of a HPKP "enforcing" header? [false] -->
+ <!-- -->
+ <!-- hpkpMaxAgeSeconds The max age value that should be used in the -->
+ <!-- HPKP header. Negative values will be treated -->
+ <!-- as zero. [0] -->
+ <!-- -->
+ <!-- hpkpIncludeSubDomains -->
+ <!-- Should the includeSubDomains parameter be -->
+ <!-- included in the HPKP header. [false] -->
+ <!-- -->
+ <!-- hpkpReportUri The URI that should be used in the HPKP -->
+ <!-- header's report URI directive. [] -->
+ <!-- -->
+ <!-- hpkpPin1 A sequence of Base64 digits representing a -->
+ <!-- SPKI fingerprint, that should be used in a -->
+ <!-- HPKP header's Pin directive. [] -->
+ <!-- -->
+ <!-- hpkpPin2 A sequence of Base64 digits representing a -->
+ <!-- SPKI fingerprint, that should be used in a -->
+ <!-- HPKP header's Pin directive. [] -->
+ <!-- -->
+ <!-- hpkpPin3 A sequence of Base64 digits representing a -->
+ <!-- SPKI fingerprint, that should be used in a -->
+ <!-- HPKP header's Pin directive. [] -->
+ <!-- -->
<!-- antiClickJackingEnabled -->
<!-- Should the anti click-jacking header -->
<!-- X-Frame-Options be added to every response? -->
Index: java/org/apache/catalina/filters/HttpHeaderSecurityFilter.java
===================================================================
--- java/org/apache/catalina/filters/HttpHeaderSecurityFilter.java (revision 1734844)
+++ java/org/apache/catalina/filters/HttpHeaderSecurityFilter.java (working copy)
@@ -45,6 +45,19 @@
private boolean hstsIncludeSubDomains = false;
private String hstsHeaderValue;
+ // HPKP
+ private static final String HPKP_HEADER_NAME = "Public-Key-Pins";
+ private static final String HPKP_RO_HEADER_NAME = "Public-Key-Pins-Report-Only";
+ private boolean hpkpEnabled = false;
+ private boolean hpkpReportOnly = false;
+ private int hpkpMaxAgeSeconds = 0;
+ private boolean hpkpIncludeSubDomains = false;
+ private String hpkpReportUri = null;
+ private String hpkpPin1 = null;
+ private String hpkpPin2 = null;
+ private String hpkpPin3 = null;
+ private String hpkpHeaderValue;
+
// Click-jacking protection
private static final String ANTI_CLICK_JACKING_HEADER_NAME = "X-Frame-Options";
private boolean antiClickJackingEnabled = true;
@@ -74,6 +87,34 @@
}
hstsHeaderValue = hstsValue.toString();
+ // Build HPKP header value
+ StringBuilder hpkpValue = new StringBuilder("max-age=");
+ hpkpValue.append(hpkpMaxAgeSeconds);
+ if (hpkpIncludeSubDomains) {
+ hpkpValue.append("; includeSubDomains");
+ }
+ if (hpkpReportUri != null) {
+ hpkpValue.append("; report-uri=\"");
+ hpkpValue.append(hpkpReportUri);
+ hpkpValue.append("\"");
+ }
+ if (hpkpPin1 != null) {
+ hpkpValue.append("; pin-sha256=\"");
+ hpkpValue.append(hpkpPin1);
+ hpkpValue.append("\"");
+ }
+ if (hpkpPin2 != null) {
+ hpkpValue.append("; pin-sha256=\"");
+ hpkpValue.append(hpkpPin2);
+ hpkpValue.append("\"");
+ }
+ if (hpkpPin3 != null) {
+ hpkpValue.append("; pin-sha256=\"");
+ hpkpValue.append(hpkpPin3);
+ hpkpValue.append("\"");
+ }
+ hpkpHeaderValue = hpkpValue.toString();
+
// Anti click-jacking
StringBuilder cjValue = new StringBuilder(antiClickJackingOption.headerValue);
if (antiClickJackingOption == XFrameOption.ALLOW_FROM) {
@@ -100,6 +141,15 @@
httpResponse.setHeader(HSTS_HEADER_NAME, hstsHeaderValue);
}
+ // HPKP
+ if (hpkpEnabled && request.isSecure()) {
+ if (hpkpReportOnly) {
+ httpResponse.setHeader(HPKP_RO_HEADER_NAME, hpkpHeaderValue);
+ } else {
+ httpResponse.setHeader(HPKP_HEADER_NAME, hpkpHeaderValue);
+ }
+ }
+
// anti click-jacking
if (antiClickJackingEnabled) {
httpResponse.setHeader(ANTI_CLICK_JACKING_HEADER_NAME, antiClickJackingHeaderValue);
@@ -169,7 +219,90 @@
}
+ public boolean isHpkpEnabled() {
+ return hpkpEnabled;
+ }
+
+ public void setHpkpEnabled(boolean hpkpEnabled) {
+ this.hpkpEnabled = hpkpEnabled;
+ }
+
+
+ public boolean isHpkpReportOnly() {
+ return hpkpReportOnly;
+ }
+
+
+ public void setHpkpReportOnly(boolean hpkpReportOnly) {
+ this.hpkpReportOnly = hpkpReportOnly;
+ }
+
+
+ public int getHpkpMaxAgeSeconds() {
+ return hpkpMaxAgeSeconds;
+ }
+
+
+ public void setHpkpMaxAgeSeconds(int hpkpMaxAgeSeconds) {
+ if (hpkpMaxAgeSeconds < 0) {
+ this.hpkpMaxAgeSeconds = 0;
+ } else {
+ this.hpkpMaxAgeSeconds = hpkpMaxAgeSeconds;
+ }
+ }
+
+
+ public boolean isHpkpIncludeSubDomains() {
+ return hpkpIncludeSubDomains;
+ }
+
+
+ public void setHpkpIncludeSubDomains(boolean hpkpIncludeSubDomains) {
+ this.hpkpIncludeSubDomains = hpkpIncludeSubDomains;
+ }
+
+
+ public String getHpkpReportUri() {
+ return this.hpkpReportUri;
+ }
+
+
+ public void setHpkpReportUri(String hpkpReportUri) {
+ this.hpkpReportUri = hpkpReportUri;
+ }
+
+
+ public String getHpkpPin1() {
+ return this.hpkpPin1;
+ }
+
+
+ public void setHpkpPin1(String hpkpPin1) {
+ this.hpkpPin1 = hpkpPin1;
+ }
+
+
+ public String getHpkpPin2() {
+ return this.hpkpPin2;
+ }
+
+
+ public void setHpkpPin2(String hpkpPin2) {
+ this.hpkpPin2 = hpkpPin2;
+ }
+
+
+ public String getHpkpPin3() {
+ return this.hpkpPin3;
+ }
+
+
+ public void setHpkpPin3(String hpkpPin3) {
+ this.hpkpPin3 = hpkpPin3;
+ }
+
+
public boolean isAntiClickJackingEnabled() {
return antiClickJackingEnabled;
}
Index: webapps/docs/config/filter.xml
===================================================================
--- webapps/docs/config/filter.xml (revision 1734844)
+++ webapps/docs/config/filter.xml (working copy)
@@ -899,6 +899,63 @@
be used.</p>
</attribute>
+ <attribute name="hpkpEnabled" required="false">
+ <p>Will an HTTP Public Key Pinning (HPKP) header (either
+ <code>Public-Key-Pins</code> or
+ <code>Public-Key-Pins-Report-Only</code>) be set on the response for
+ secure requests. Any HPKP header already present will be replaced. See
+ <a href="http://tools.ietf.org/html/rfc7469">RFC 7469</a> for further
+ details of HPKP. If not specified, the default value of
+ <code>false</code> will be used.</p>
+ </attribute>
+
+ <attribute name="hpkpReportOnly" required="false">
+ <p>Will a HPKP "report only" header
+ (<code>Public-Key-Pins-Report-Only</code>) be set instead of a HPKP
+ "enforcing" header (<code>Public-Key-Pins</code>). If not specified,
+ the default value of <code>false</code> will be used.
+ </p>
+ </attribute>
+
+ <attribute name="hpkpMaxAgeSeconds" required="false">
+ <p>The max age value that should be used in the HPKP header. Negative
+ values will be treated as zero. If not specified, the default value of
+ <code>0</code> will be used.</p>
+ </attribute>
+
+ <attribute name="hpkpIncludeSubDomains" required="false">
+ <p>Should the includeSubDomains parameter be included in the HSTS
+ header. If not specified, the default value of <code>false</code> will
+ be used.</p>
+ </attribute>
+
+ <attribute name="hpkpReportUri" required="false">
+ <p>The URI that should be used in the HPKP header's report URI
+ directive (<code>report-uri</code>). If not specified, the directive
+ will be omitted in the HPKP header.</p>
+ </attribute>
+
+ <attribute name="hpkpPin1" required="false">
+ <p>A sequence of Base64 digits representing a SPKI fingerprint, that
+ should be used in a HPKP header's Pin directive
+ (<code>pin-sha256</code>). If not specified, the directive will be
+ omitted in the HPKP header.</p>
+ </attribute>
+
+ <attribute name="hpkpPin2" required="false">
+ <p>A sequence of Base64 digits representing a SPKI fingerprint, that
+ should be used in a HPKP header's Pin directive
+ (<code>pin-sha256</code>). If not specified, the directive will be
+ omitted in the HPKP header.</p>
+ </attribute>
+
+ <attribute name="hpkpPin3" required="false">
+ <p>A sequence of Base64 digits representing a SPKI fingerprint, that
+ should be used in a HPKP header's Pin directive
+ (<code>pin-sha256</code>). If not specified, the directive will be
+ omitted in the HPKP header.</p>
+ </attribute>
+
<attribute name="antiClickJackingEnabled" required="false">
<p>Should the anti click-jacking header (<code>X-Frame-Options</code>)
be set on the response. Any anti click-jacking header already present
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]