diff --git a/server/bootstrap/src/org/labkey/filters/ContentSecurityPolicyFilter.java b/server/bootstrap/src/org/labkey/filters/ContentSecurityPolicyFilter.java
deleted file mode 100644
index 3c666b0b88..0000000000
--- a/server/bootstrap/src/org/labkey/filters/ContentSecurityPolicyFilter.java
+++ /dev/null
@@ -1,213 +0,0 @@
-package org.labkey.filters;
-
-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;
-import java.io.IOException;
-import java.security.SecureRandom;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
-
-
-/** example usage,
-
- NOTE: LabKey does not yet support setting the "Report-To" header, so we do not support the report-to CSP directive.
-
- Example 1 : very strict, disallows 'external' websites, disallows unsafe-inline, but only reports violations (does not enforce)
- good for test automation!
-
-
-
- Content Security Policy Filter Filter
- org.labkey.filters.ContentSecurityPolicyFilter
-
- policy
-
- default-src 'self';
- connect-src 'self' ${LABKEY.ALLOWED.CONNECTIONS} ;
- object-src 'none' ;
- style-src 'self' 'unsafe-inline' ;
- img-src 'self' data: ;
- font-src 'self' data: ;
- script-src 'unsafe-eval' 'strict-dynamic' 'nonce-${REQUEST.SCRIPT.NONCE}';
- base-uri 'self' ;
- upgrade-insecure-requests ;
- frame-ancestors 'self' ;
- report-uri /labkey/admin-contentsecuritypolicyreport.api ;
-
-
-
- disposition
- report
-
-
-
- Content Security Policy Filter Filter
- /*
-
-
-
- Example 2 : less strict but enforces directives, (NOTE: unsafe-inline is still required for many modules)
-
-
-
- Content Security Policy Filter Filter
- org.labkey.filters.ContentSecurityPolicyFilter
-
- policy
-
- default-src 'self' https: ;
- connect-src 'self' https: ${LABKEY.ALLOWED.CONNECTIONS} ;
- object-src 'none' ;
- style-src 'self' https: 'unsafe-inline' ;
- img-src 'self' data: ;
- font-src 'self' data: ;
- script-src 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' 'nonce-${REQUEST.SCRIPT.NONCE}';
- base-uri 'self' ;
- upgrade-insecure-requests ;
- frame-ancestors 'self' ;
- report-uri /labkey/admin-contentsecuritypolicyreport.api ;
-
-
-
- disposition
- enforce
-
-
-
- Content Security Policy Filter Filter
- /*
-
-
-
- Do not copy-and-paste these examples for any production environment without understanding the meaning of each directive!
- */
-
-
-public class ContentSecurityPolicyFilter implements Filter
-{
- private static final String NONCE_SUBST = "${REQUEST.SCRIPT.NONCE}";
- private static final String ALLOWED_CONNECT_SUBSTITUTION = "${LABKEY.ALLOWED.CONNECTIONS}";
- private static final String HEADER_NONCE = "org.labkey.filters.ContentSecurityPolicyFilter#NONCE"; // needs to match PageConfig.HEADER_NONCE
- private static final String CONTENT_SECURITY_POLICY_HEADER_NAME = "Content-Security-Policy";
- private static final String CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER_NAME = "Content-Security-Policy-Report-Only";
- private String policy = "";
- private int nonceSubstIndex = -1;
- private int allowedConnectionSubstitutionIndex = -1;
- private static String connectionSrc = "";
-
- private static final Map allowedConnectionSources = new ConcurrentHashMap<>();
- private boolean reportOnly = false;
-
- @Override
- public void init(FilterConfig filterConfig) throws ServletException
- {
- Enumeration paramNames = filterConfig.getInitParameterNames();
- while (paramNames.hasMoreElements())
- {
- String paramName = paramNames.nextElement();
- String paramValue = filterConfig.getInitParameter(paramName);
- if ("policy".equalsIgnoreCase(paramName))
- {
- String s = paramValue.trim();
- s = s.replace( '\n', ' ' );
- s = s.replace( '\r', ' ' );
- s = s.replace( '\t', ' ' );
- s = s.replace((char)0x2018, (char)0x027); // LEFT SINGLE QUOTATION MARK -> APOSTROPHE
- s = s.replace((char)0x2019, (char)0x027); // RIGHT SINGLE QUOTATION MARK -> APOSTROPHE
- policy = s;
- nonceSubstIndex = policy.indexOf(NONCE_SUBST);
- allowedConnectionSubstitutionIndex = policy.indexOf(ALLOWED_CONNECT_SUBSTITUTION);
- }
- else if ("disposition".equalsIgnoreCase(paramName))
- {
- String s = paramValue.trim();
- if (!"report".equalsIgnoreCase(s) && !"enforce".equalsIgnoreCase(s))
- throw new ServletException("ContentSecurityPolicyFilter is misconfigured, unexpected disposition value: " + s);
- reportOnly = "report".equalsIgnoreCase(s);
- }
- else
- {
- throw new ServletException("ContentSecurityPolicyFilter is misconfigured, unexpected parameter name: " + paramName);
- }
- }
- }
-
-
- @Override
- public void destroy()
- {
- }
-
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
- {
- if (request instanceof HttpServletRequest req && response instanceof HttpServletResponse resp && null != policy && policy.length()>0)
- {
- var csp = policy;
- if (nonceSubstIndex != -1)
- csp = csp.substring(0,nonceSubstIndex) + getScriptNonceHeader(req) + csp.substring(nonceSubstIndex + NONCE_SUBST.length());
-
- if (allowedConnectionSubstitutionIndex != -1 )
- {
- csp = csp.substring(0,allowedConnectionSubstitutionIndex)
- + connectionSrc
- + csp.substring(allowedConnectionSubstitutionIndex + ALLOWED_CONNECT_SUBSTITUTION.length());
- }
- var header = reportOnly ? CONTENT_SECURITY_POLICY_REPORT_ONLY_HEADER_NAME : CONTENT_SECURITY_POLICY_HEADER_NAME;
- resp.setHeader(header, csp);
- }
- chain.doFilter(request, response);
- }
-
- /**
- * Return concatenated list of allowed connection hosts
- * @param allowedConnectionSources
- * @return
- */
- private static String getAllowedConnectionsHeader(Collection allowedConnectionSources)
- {
- //Remove substitution parameter if no sources are registered
- if (allowedConnectionSources.isEmpty())
- return "";
-
- return allowedConnectionSources.stream().distinct().collect(Collectors.joining(" "));
- }
-
-
- public static String getScriptNonceHeader(HttpServletRequest request)
- {
- String nonce = (String)request.getAttribute(HEADER_NONCE);
- if (nonce != null)
- return nonce;
-
- nonce = Long.toHexString(rand.nextLong());
- rand.setSeed(request.getRequestURI().hashCode());
-
- request.setAttribute(HEADER_NONCE, nonce);
- return nonce;
- }
-
- private static final SecureRandom rand = new SecureRandom();
-
- public static void registerAllowedConnectionSource(String key, String allowedUrl)
- {
- allowedConnectionSources.put(key, allowedUrl);
- connectionSrc = getAllowedConnectionsHeader(allowedConnectionSources.values());
- }
-
- public static void unregisterAllowedConnectionSource(String key)
- {
- allowedConnectionSources.remove(key);
- connectionSrc = getAllowedConnectionsHeader(allowedConnectionSources.values());
- }
-}
diff --git a/server/embedded/src/org/labkey/embedded/LabKeyServer.java b/server/embedded/src/org/labkey/embedded/LabKeyServer.java
index ae6d1c33bd..279ecf55bf 100644
--- a/server/embedded/src/org/labkey/embedded/LabKeyServer.java
+++ b/server/embedded/src/org/labkey/embedded/LabKeyServer.java
@@ -8,7 +8,6 @@
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import org.labkey.bootstrap.ConfigException;
-import org.labkey.filters.ContentSecurityPolicyFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.ApplicationPidFileWriter;
@@ -130,10 +129,12 @@ protected TomcatWebServer getTomcatWebServer(Tomcat tomcat)
if (cspFilterProperties.getEnforce() != null)
{
+ context.addParameter("csp.enforce", cspFilterProperties.getEnforce());
addCSPFilter("enforce", cspFilterProperties.getEnforce(), ENFORCE_CSP_FILTER_NAME ,context);
}
if (cspFilterProperties.getReport() != null)
{
+ context.addParameter("csp.report", cspFilterProperties.getReport());
addCSPFilter("report", cspFilterProperties.getReport(), REPORT_CSP_FILTER_NAME, context);
}
@@ -199,7 +200,7 @@ private void addCSPFilter(String disposition, String policy, String filterName,
{
FilterDef filterDef = new FilterDef();
filterDef.setFilterName(filterName);
- filterDef.setFilter(new ContentSecurityPolicyFilter());
+// filterDef.setFilter(new ContentSecurityPolicyFilter());
filterDef.addInitParameter("policy", policy);
filterDef.addInitParameter("disposition", disposition);