diff --git a/modules/perc-security-utils/src/main/java/com/percussion/security/servlet/PSSecurityHeadersFilter.java b/modules/perc-security-utils/src/main/java/com/percussion/security/servlet/PSSecurityHeadersFilter.java new file mode 100644 index 0000000000..f167c6b452 --- /dev/null +++ b/modules/perc-security-utils/src/main/java/com/percussion/security/servlet/PSSecurityHeadersFilter.java @@ -0,0 +1,103 @@ +/* + * Copyright 1999-2026 Percussion Software, Inc. + * + * 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 + * + * http://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 com.percussion.security.servlet; + +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Servlet filter that sets standard security response headers on every request. Defense in depth + * for several of the 45+ CVEs in Spring 5.3.x + Spring Security 5.8.x (parent epic #73, T2.4 + * sub-task). + * + *

Headers set: + * + *

+ * + *

All values are constants on the class. Future tuning is a one-line change. + * + *

To register in web.xml: + * + *

+ *   <filter>
+ *     <filter-name>PSSecurityHeadersFilter</filter-name>
+ *     <filter-class>com.percussion.security.servlet.PSSecurityHeadersFilter</filter-class>
+ *   </filter>
+ *   <filter-mapping>
+ *     <filter-name>PSSecurityHeadersFilter</filter-name>
+ *     <url-pattern>/*</url-pattern>
+ *   </filter-mapping>
+ * 
+ */ +public class PSSecurityHeadersFilter implements Filter { + + private static final String X_FRAME_OPTIONS = "X-Frame-Options"; + private static final String X_CONTENT_TYPE_OPTIONS = "X-Content-Type-Options"; + private static final String REFERRER_POLICY = "Referrer-Policy"; + private static final String STRICT_TRANSPORT_SECURITY = "Strict-Transport-Security"; + private static final String X_XSS_PROTECTION = "X-XSS-Protection"; + + private static final String VALUE_SAMEORIGIN = "SAMEORIGIN"; + private static final String VALUE_NOSNIFF = "nosniff"; + private static final String VALUE_REFERRER_POLICY = "strict-origin-when-cross-origin"; + private static final String VALUE_HSTS = "max-age=31536000; includeSubDomains"; + private static final String VALUE_XSS_PROTECTION_DISABLED = "0"; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + // No init params required; values are class constants. + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + if (response instanceof HttpServletResponse) { + HttpServletResponse httpResponse = (HttpServletResponse) response; + // SetHeader (not addHeader) so a downstream filter / handler can override. + httpResponse.setHeader(X_FRAME_OPTIONS, VALUE_SAMEORIGIN); + httpResponse.setHeader(X_CONTENT_TYPE_OPTIONS, VALUE_NOSNIFF); + httpResponse.setHeader(REFERRER_POLICY, VALUE_REFERRER_POLICY); + httpResponse.setHeader(X_XSS_PROTECTION, VALUE_XSS_PROTECTION_DISABLED); + + // HSTS only on HTTPS — see class javadoc. + if (request instanceof HttpServletRequest && ((HttpServletRequest) request).isSecure()) { + httpResponse.setHeader(STRICT_TRANSPORT_SECURITY, VALUE_HSTS); + } + } + chain.doFilter(request, response); + } + + @Override + public void destroy() { + // Nothing to clean up. + } +} diff --git a/projects/sitemanage/src/main/resources/Rhythmyx/sys_resources/webapps/non-secure/WEB-INF/web.xml b/projects/sitemanage/src/main/resources/Rhythmyx/sys_resources/webapps/non-secure/WEB-INF/web.xml index 8f0b42f9a1..b1b76b0d88 100644 --- a/projects/sitemanage/src/main/resources/Rhythmyx/sys_resources/webapps/non-secure/WEB-INF/web.xml +++ b/projects/sitemanage/src/main/resources/Rhythmyx/sys_resources/webapps/non-secure/WEB-INF/web.xml @@ -22,6 +22,20 @@ /* + + + PSSecurityHeadersFilter + com.percussion.security.servlet.PSSecurityHeadersFilter + + + + PSSecurityHeadersFilter + /* + + CorsFilter org.apache.catalina.filters.CorsFilter diff --git a/projects/sitemanage/src/main/resources/Rhythmyx/sys_resources/webapps/secure/WEB-INF/web.xml b/projects/sitemanage/src/main/resources/Rhythmyx/sys_resources/webapps/secure/WEB-INF/web.xml index f0676812fa..b3da24f2d1 100644 --- a/projects/sitemanage/src/main/resources/Rhythmyx/sys_resources/webapps/secure/WEB-INF/web.xml +++ b/projects/sitemanage/src/main/resources/Rhythmyx/sys_resources/webapps/secure/WEB-INF/web.xml @@ -49,6 +49,20 @@ /* + + + PSSecurityHeadersFilter + com.percussion.security.servlet.PSSecurityHeadersFilter + + + + PSSecurityHeadersFilter + /* + + org.springframework.web.context.ContextLoaderListener