Polish HPKP

* Javadoc polish
* Whitespace cleanup

Issue gh-3706
This commit is contained in:
Rob Winch 2016-03-03 15:11:40 -06:00
parent a7b0f74803
commit db81977a1a
6 changed files with 196 additions and 195 deletions

View File

@ -166,7 +166,6 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
loadParsers();
}
@SuppressWarnings("deprecation")
private void loadParsers() {
// Parsers
parsers.put(Elements.LDAP_PROVIDER, new LdapProviderBeanDefinitionParser());

View File

@ -218,11 +218,11 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
hash = "sha256";
}
Node pinValueNode = pinElement.getFirstChild();
if (pinValueNode == null) {
context.getReaderContext().warning("Missing value for pin entry.", hpkpElement);
continue;
}
Node pinValueNode = pinElement.getFirstChild();
if (pinValueNode == null) {
context.getReaderContext().warning("Missing value for pin entry.", hpkpElement);
continue;
}
String fingerprint = pinElement.getFirstChild().getTextContent();

View File

@ -35,12 +35,12 @@ class HeadersConfigurerTests extends BaseSpringSpec {
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['X-Content-Type-Options':'nosniff',
'X-Frame-Options':'DENY',
'Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains',
'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
'Expires' : '0',
'Pragma':'no-cache',
'X-XSS-Protection' : '1; mode=block']
'X-Frame-Options':'DENY',
'Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains',
'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
'Expires' : '0',
'Pragma':'no-cache',
'X-XSS-Protection' : '1; mode=block']
}
@EnableWebSecurity
@ -123,8 +123,8 @@ class HeadersConfigurerTests extends BaseSpringSpec {
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
'Expires' : '0',
'Pragma':'no-cache']
'Expires' : '0',
'Pragma':'no-cache']
}
@EnableWebSecurity
@ -168,12 +168,12 @@ class HeadersConfigurerTests extends BaseSpringSpec {
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['X-Content-Type-Options':'nosniff',
'X-Frame-Options':'SAMEORIGIN',
'Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains',
'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
'Expires' : '0',
'Pragma':'no-cache',
'X-XSS-Protection' : '1; mode=block']
'X-Frame-Options':'SAMEORIGIN',
'Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains',
'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
'Expires' : '0',
'Pragma':'no-cache',
'X-XSS-Protection' : '1; mode=block']
}
@EnableWebSecurity

View File

@ -30,12 +30,12 @@ import org.springframework.security.web.util.matcher.AnyRequestMatcher
*/
class HttpHeadersConfigTests extends AbstractHttpConfigTests {
def defaultHeaders = ['X-Content-Type-Options':'nosniff',
'X-Frame-Options':'DENY',
'Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains',
'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
'Expires' : '0',
'Pragma':'no-cache',
'X-XSS-Protection' : '1; mode=block']
'X-Frame-Options':'DENY',
'Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains',
'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
'Expires' : '0',
'Pragma':'no-cache',
'X-XSS-Protection' : '1; mode=block']
def 'headers disabled'() {
setup:
httpAutoConfig {
@ -294,7 +294,7 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
MockHttpServletResponse response = new MockHttpServletResponse()
hf.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
then:
assertHeaders(response, ['abc':'def'])
assertHeaders(response, ['abc':'def'])
}
def 'http headers header no name produces error'() {
@ -404,8 +404,8 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
springSecurityFilterChain.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
then:
assertHeaders(response, ['Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
'Expires' : '0',
'Pragma':'no-cache'])
'Expires' : '0',
'Pragma':'no-cache'])
}
def 'http headers hsts'() {
@ -458,35 +458,35 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
assertHeaders(response, ['Strict-Transport-Security': 'max-age=1'])
}
def 'http headers hpkp no pins'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'()
}
}
when:
createAppContext()
then:
XmlBeanDefinitionStoreException expected = thrown()
expected.message.contains 'The content of element \'hpkp\' is not complete'
}
def 'http headers hpkp no pins'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'()
}
}
when:
createAppContext()
then:
XmlBeanDefinitionStoreException expected = thrown()
expected.message.contains 'The content of element \'hpkp\' is not complete'
}
def 'http headers hpkp no pin'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'() {
'pins'()
}
}
}
when:
createAppContext()
then:
XmlBeanDefinitionStoreException expected = thrown()
expected.message.contains 'The content of element \'pins\' is not complete'
}
def 'http headers hpkp no pin'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'() {
'pins'()
}
}
}
when:
createAppContext()
then:
XmlBeanDefinitionStoreException expected = thrown()
expected.message.contains 'The content of element \'pins\' is not complete'
}
def 'http headers hpkp'() {
setup:
@ -508,125 +508,125 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
assertHeaders(response, ['Public-Key-Pins-Report-Only': 'max-age=5184000 ; pin-sha256="d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM="'])
}
def 'http headers hpkp with default algorithm'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'() {
'pins'() {
'pin'('d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=')
}
}
}
}
createAppContext()
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
MockHttpServletResponse response = new MockHttpServletResponse()
when:
springSecurityFilterChain.doFilter(new MockHttpServletRequest(secure:true), response, new MockFilterChain())
then:
assertHeaders(response, ['Public-Key-Pins-Report-Only': 'max-age=5184000 ; pin-sha256="d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM="'])
}
def 'http headers hpkp with default algorithm'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'() {
'pins'() {
'pin'('d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=')
}
}
}
}
createAppContext()
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
MockHttpServletResponse response = new MockHttpServletResponse()
when:
springSecurityFilterChain.doFilter(new MockHttpServletRequest(secure:true), response, new MockFilterChain())
then:
assertHeaders(response, ['Public-Key-Pins-Report-Only': 'max-age=5184000 ; pin-sha256="d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM="'])
}
def 'http headers hpkp only invokes on HttpServletRequest.isSecure = true'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'() {
'pins'() {
'pin'('algorithm':'sha256', 'E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=')
}
}
}
}
createAppContext()
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
MockHttpServletResponse response = new MockHttpServletResponse()
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'() {
'pins'() {
'pin'('algorithm':'sha256', 'E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=')
}
}
}
}
createAppContext()
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
MockHttpServletResponse response = new MockHttpServletResponse()
when:
springSecurityFilterChain.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
springSecurityFilterChain.doFilter(new MockHttpServletRequest(), response, new MockFilterChain())
then:
response.headerNames.empty
response.headerNames.empty
}
def 'http headers hpkp with custom max age'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'('max-age-seconds':'604800') {
'pins'() {
'pin'('algorithm':'sha256', 'd6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=')
}
}
}
}
createAppContext()
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
MockHttpServletResponse response = new MockHttpServletResponse()
when:
springSecurityFilterChain.doFilter(new MockHttpServletRequest(secure:true), response, new MockFilterChain())
then:
assertHeaders(response, ['Public-Key-Pins-Report-Only': 'max-age=604800 ; pin-sha256="d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM="'])
}
def 'http headers hpkp with custom max age'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'('max-age-seconds':'604800') {
'pins'() {
'pin'('algorithm':'sha256', 'd6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=')
}
}
}
}
createAppContext()
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
MockHttpServletResponse response = new MockHttpServletResponse()
when:
springSecurityFilterChain.doFilter(new MockHttpServletRequest(secure:true), response, new MockFilterChain())
then:
assertHeaders(response, ['Public-Key-Pins-Report-Only': 'max-age=604800 ; pin-sha256="d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM="'])
}
def 'http headers hpkp@reportOnly=false'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'('report-only':'false') {
'pins'() {
'pin'('algorithm':'sha256', 'E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=')
}
}
}
}
createAppContext()
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
MockHttpServletResponse response = new MockHttpServletResponse()
when:
springSecurityFilterChain.doFilter(new MockHttpServletRequest(secure: true), response, new MockFilterChain())
then:
assertHeaders(response, ['Public-Key-Pins': 'max-age=5184000 ; pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g="'])
}
def 'http headers hpkp@reportOnly=false'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'('report-only':'false') {
'pins'() {
'pin'('algorithm':'sha256', 'E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=')
}
}
}
}
createAppContext()
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
MockHttpServletResponse response = new MockHttpServletResponse()
when:
springSecurityFilterChain.doFilter(new MockHttpServletRequest(secure: true), response, new MockFilterChain())
then:
assertHeaders(response, ['Public-Key-Pins': 'max-age=5184000 ; pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g="'])
}
def 'http headers hpkp@includeSubDomains=true'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'('include-subdomains':'true') {
'pins'() {
'pin'('algorithm':'sha256', 'E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=')
}
}
}
}
createAppContext()
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
MockHttpServletResponse response = new MockHttpServletResponse()
when:
springSecurityFilterChain.doFilter(new MockHttpServletRequest(secure: true), response, new MockFilterChain())
then:
assertHeaders(response, ['Public-Key-Pins-Report-Only': 'max-age=5184000 ; pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" ; includeSubDomains'])
}
def 'http headers hpkp@includeSubDomains=true'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'('include-subdomains':'true') {
'pins'() {
'pin'('algorithm':'sha256', 'E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=')
}
}
}
}
createAppContext()
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
MockHttpServletResponse response = new MockHttpServletResponse()
when:
springSecurityFilterChain.doFilter(new MockHttpServletRequest(secure: true), response, new MockFilterChain())
then:
assertHeaders(response, ['Public-Key-Pins-Report-Only': 'max-age=5184000 ; pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" ; includeSubDomains'])
}
def 'http headers hpkp with report-uri'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'('report-uri':'http://example.net/pkp-report') {
'pins'() {
'pin'('algorithm':'sha256', 'E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=')
}
}
}
}
createAppContext()
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
MockHttpServletResponse response = new MockHttpServletResponse()
when:
springSecurityFilterChain.doFilter(new MockHttpServletRequest(secure: true), response, new MockFilterChain())
then:
assertHeaders(response, ['Public-Key-Pins-Report-Only': 'max-age=5184000 ; pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" ; report-uri="http://example.net/pkp-report"'])
}
def 'http headers hpkp with report-uri'() {
setup:
httpAutoConfig {
'headers'('defaults-disabled':true) {
'hpkp'('report-uri':'http://example.net/pkp-report') {
'pins'() {
'pin'('algorithm':'sha256', 'E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=')
}
}
}
}
createAppContext()
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
MockHttpServletResponse response = new MockHttpServletResponse()
when:
springSecurityFilterChain.doFilter(new MockHttpServletRequest(secure: true), response, new MockFilterChain())
then:
assertHeaders(response, ['Public-Key-Pins-Report-Only': 'max-age=5184000 ; pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" ; report-uri="http://example.net/pkp-report"'])
}
// --- disable single default header ---
@ -688,23 +688,23 @@ class HttpHeadersConfigTests extends AbstractHttpConfigTests {
def 'http headers hpkp@disabled=true'() {
setup:
httpAutoConfig {
'headers'() {
'hpkp'(disabled:true) {
'pins'() {
'pin'('algorithm':'sha256', 'E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=')
}
}
}
}
createAppContext()
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
MockHttpServletResponse response = new MockHttpServletResponse()
def expectedHeaders = [:] << defaultHeaders
httpAutoConfig {
'headers'() {
'hpkp'(disabled:true) {
'pins'() {
'pin'('algorithm':'sha256', 'E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=')
}
}
}
}
createAppContext()
def springSecurityFilterChain = appContext.getBean(FilterChainProxy)
MockHttpServletResponse response = new MockHttpServletResponse()
def expectedHeaders = [:] << defaultHeaders
when:
springSecurityFilterChain.doFilter(new MockHttpServletRequest(secure:true), response, new MockFilterChain())
springSecurityFilterChain.doFilter(new MockHttpServletRequest(secure:true), response, new MockFilterChain())
then:
assertHeaders(response, expectedHeaders)
assertHeaders(response, expectedHeaders)
}
def 'http headers frame-options@disabled=true'() {

View File

@ -3844,8 +3844,8 @@ Opposed to the other headers, Spring Security does not add HPKP by default. You
include-subdomains="true"
report-uri="http://example.net/pkp-report">
<pins>
<pin algorithm="sha256">d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=</pin>
<pin algorithm="sha256">E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=</pin>
<pin algorithm="sha256">d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=</pin>
<pin algorithm="sha256">E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=</pin>
</pins>
</hpkp>
</headers>
@ -3860,16 +3860,16 @@ Similarly, you can enable HPKP headers with Java Configuration:
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers()
.httpPublicKeyPinning()
.includeSubdomains(true)
.reportUri("http://example.net/pkp-report")
.addSha256Pins("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=", "E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=";
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers()
.httpPublicKeyPinning()
.includeSubdomains(true)
.reportUri("http://example.net/pkp-report")
.addSha256Pins("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=", "E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=";
}
}
----

View File

@ -162,7 +162,7 @@ public final class HpkpHeaderWriter implements HeaderWriter {
/*
* (non-Javadoc)
*
*
* @see org.springframework.security.web.headers.HeaderWriter#writeHeaders(javax
* .servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@ -199,9 +199,11 @@ public final class HpkpHeaderWriter implements HeaderWriter {
*
* Use
*
* Map<String, String> pins = new HashMap<String, String>();
* <code>
* Map&lt;String, String&gt; pins = new HashMap&lt;String, String&gt;();
* pins.put("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=", "sha256");
* pins.put("E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=", "sha256");
* </code>
* </p>
*
* @param pins the map of base64-encoded SPKI fingerprint &amp; cryptographic hash algorithm pairs.