All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.security.web.header.writers.HstsHeaderWriter Maven / Gradle / Ivy

/*
 * Copyright 2002-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.security.web.header.writers;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.core.log.LogMessage;
import org.springframework.security.web.header.HeaderWriter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;

/**
 * Provides support for HTTP Strict
 * Transport Security (HSTS).
 *
 * 

* By default the expiration is one year, subdomains will be included and preload will not * be included. This can be customized using {@link #setMaxAgeInSeconds(long)}, * {@link #setIncludeSubDomains(boolean)} and {@link #setPreload(boolean)} respectively. *

* *

* Since section 7.2 states * that HSTS Host MUST NOT include the STS header in HTTP responses, the default behavior * is that the "Strict-Transport-Security" will only be added when * {@link HttpServletRequest#isSecure()} returns {@code true} . At times this may need to * be customized. For example, in some situations where SSL termination is used, something * else may be used to determine if SSL was used. For these circumstances, * {@link #setRequestMatcher(RequestMatcher)} can be invoked with a custom * {@link RequestMatcher}. *

* *

* See Website hstspreload.org for additional * details on HSTS preload. *

* * @author Rob Winch * @author Ankur Pathak * @since 3.2 */ public final class HstsHeaderWriter implements HeaderWriter { private static final long DEFAULT_MAX_AGE_SECONDS = 31536000; private static final String HSTS_HEADER_NAME = "Strict-Transport-Security"; private final Log logger = LogFactory.getLog(getClass()); private RequestMatcher requestMatcher; private long maxAgeInSeconds; private boolean includeSubDomains; private boolean preload; private String hstsHeaderValue; /** * Creates a new instance * @param requestMatcher maps to {@link #setRequestMatcher(RequestMatcher)} * @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)} * @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)} * @param preload maps to {@link #setPreload(boolean)} * @since 5.2.0 * @author Ankur Pathak */ public HstsHeaderWriter(RequestMatcher requestMatcher, long maxAgeInSeconds, boolean includeSubDomains, boolean preload) { this.requestMatcher = requestMatcher; this.maxAgeInSeconds = maxAgeInSeconds; this.includeSubDomains = includeSubDomains; this.preload = preload; updateHstsHeaderValue(); } /** * Creates a new instance * @param requestMatcher maps to {@link #setRequestMatcher(RequestMatcher)} * @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)} * @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)} */ public HstsHeaderWriter(RequestMatcher requestMatcher, long maxAgeInSeconds, boolean includeSubDomains) { this(requestMatcher, maxAgeInSeconds, includeSubDomains, false); } /** * Creates a new instance * @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)} * @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)} * @param preload maps to {@link #setPreload(boolean)} * @since 5.2.0 * @author Ankur Pathak */ public HstsHeaderWriter(long maxAgeInSeconds, boolean includeSubDomains, boolean preload) { this(new SecureRequestMatcher(), maxAgeInSeconds, includeSubDomains, preload); } /** * Creates a new instance * @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)} * @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)} */ public HstsHeaderWriter(long maxAgeInSeconds, boolean includeSubDomains) { this(new SecureRequestMatcher(), maxAgeInSeconds, includeSubDomains, false); } /** * Creates a new instance * @param maxAgeInSeconds maps to {@link #setMaxAgeInSeconds(long)} */ public HstsHeaderWriter(long maxAgeInSeconds) { this(new SecureRequestMatcher(), maxAgeInSeconds, true, false); } /** * Creates a new instance * @param includeSubDomains maps to {@link #setIncludeSubDomains(boolean)} */ public HstsHeaderWriter(boolean includeSubDomains) { this(new SecureRequestMatcher(), DEFAULT_MAX_AGE_SECONDS, includeSubDomains, false); } /** * Creates a new instance */ public HstsHeaderWriter() { this(DEFAULT_MAX_AGE_SECONDS); } @Override public void writeHeaders(HttpServletRequest request, HttpServletResponse response) { if (!this.requestMatcher.matches(request)) { if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Not injecting HSTS header since it did not match request to [%s]", this.requestMatcher)); } return; } if (!response.containsHeader(HSTS_HEADER_NAME)) { response.setHeader(HSTS_HEADER_NAME, this.hstsHeaderValue); } } /** * Sets the {@link RequestMatcher} used to determine if the * "Strict-Transport-Security" should be added. If true the header is added, else the * header is not added. By default the header is added when * {@link HttpServletRequest#isSecure()} returns true. * @param requestMatcher the {@link RequestMatcher} to use. * @throws IllegalArgumentException if {@link RequestMatcher} is null */ public void setRequestMatcher(RequestMatcher requestMatcher) { Assert.notNull(requestMatcher, "requestMatcher cannot be null"); this.requestMatcher = requestMatcher; } /** *

* Sets the value (in seconds) for the max-age directive of the * Strict-Transport-Security header. The default is one year. *

* *

* This instructs browsers how long to remember to keep this domain as a known HSTS * Host. See Section * 6.1.1 for additional details. *

* @param maxAgeInSeconds the maximum amount of time (in seconds) to consider this * domain as a known HSTS Host. * @throws IllegalArgumentException if maxAgeInSeconds is negative */ public void setMaxAgeInSeconds(long maxAgeInSeconds) { Assert.isTrue(maxAgeInSeconds >= 0, () -> "maxAgeInSeconds must be non-negative. Got " + maxAgeInSeconds); this.maxAgeInSeconds = maxAgeInSeconds; updateHstsHeaderValue(); } /** *

* If true, subdomains should be considered HSTS Hosts too. The default is true. *

* *

* See Section 6.1.2 * for additional details. *

* @param includeSubDomains true to include subdomains, else false */ public void setIncludeSubDomains(boolean includeSubDomains) { this.includeSubDomains = includeSubDomains; updateHstsHeaderValue(); } /** *

* If true, preload will be included in HSTS Header. The default is false. *

* *

* See Section 6.1.2 * for additional details. *

* @param preload true to include preload, else false * @since 5.2.0 * @author Ankur Pathak */ public void setPreload(boolean preload) { this.preload = preload; updateHstsHeaderValue(); } private void updateHstsHeaderValue() { String headerValue = "max-age=" + this.maxAgeInSeconds; if (this.includeSubDomains) { headerValue += " ; includeSubDomains"; } if (this.preload) { headerValue += " ; preload"; } this.hstsHeaderValue = headerValue; } private static final class SecureRequestMatcher implements RequestMatcher { @Override public boolean matches(HttpServletRequest request) { return request.isSecure(); } @Override public String toString() { return "Is Secure"; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy