Skip to content

Commit 861734d

Browse files
committed
Make SecurityHeadersFilter configurable via RODA properties #3586
1 parent 4dc5d57 commit 861734d

2 files changed

Lines changed: 79 additions & 18 deletions

File tree

roda-ui/roda-wui/src/main/java/org/roda/wui/filter/SecurityHeadersFilter.java

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@
88
package org.roda.wui.filter;
99

1010
import java.io.IOException;
11+
import java.util.HashMap;
12+
import java.util.List;
13+
import java.util.Map;
14+
15+
import org.apache.commons.configuration.Configuration;
16+
import org.roda.core.RodaCoreFactory;
17+
import org.roda.core.common.RodaUtils;
18+
import org.slf4j.Logger;
19+
import org.slf4j.LoggerFactory;
1120

1221
import jakarta.servlet.Filter;
1322
import jakarta.servlet.FilterChain;
@@ -18,31 +27,45 @@
1827
import jakarta.servlet.http.HttpServletResponse;
1928

2029
public class SecurityHeadersFilter implements Filter {
30+
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityHeadersFilter.class);
31+
private final Map<String, String> headers = new HashMap<>();
32+
private final static String SECURITY_HEADER_PROPERTY_PREFIX = "ui.filter.security-headers[]";
33+
private Boolean isInit = false;
2134

2235
@Override
23-
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
24-
throws IOException, ServletException {
25-
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
36+
public void init(FilterConfig filterConfig) {
2637

27-
httpServletResponse.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
28-
httpServletResponse.setHeader("Content-Security-Policy",
29-
"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com "
30-
+ "https://www.google-analytics.com https://www.gstatic.com http://127.0.0.1:9876 http://localhost:9876; "
31-
+ "style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self';");
32-
httpServletResponse.setHeader("X-XSS-Protection", "1; mode=block");
33-
httpServletResponse.setHeader("X-Permitted-Cross-Domain-Policies", "none");
34-
httpServletResponse.setHeader("Feature-Policy",
35-
"camera 'none'; fullscreen 'self'; geolocation *; " + "microphone 'self'");
36-
httpServletResponse.setHeader("X-Frame-Options", "SAMEORIGIN");
37-
httpServletResponse.setHeader("X-Content-Type-Options", "nosniff");
38-
httpServletResponse.setHeader("Referrer-Policy", "no-referrer");
39-
httpServletResponse.setHeader("Permissions-Policy", "geolocation=(self)");
38+
}
4039

41-
chain.doFilter(request, response);
40+
private void initSecurityHeaders() {
41+
if (isInit) {
42+
return;
43+
}
44+
45+
final Configuration rodaConfig = RodaCoreFactory.getRodaConfiguration();
46+
if (rodaConfig == null) {
47+
LOGGER.info("RODA configuration not available yet. Delaying init of SecurityHeadersFilter.");
48+
} else {
49+
List<String> headersNames = RodaUtils.copyList(rodaConfig.getList(SECURITY_HEADER_PROPERTY_PREFIX));
50+
51+
for (String headersName : headersNames) {
52+
String value = rodaConfig.getString(SECURITY_HEADER_PROPERTY_PREFIX + "." + headersName, null);
53+
if (value != null) {
54+
headers.put(headersName, value);
55+
}
56+
}
57+
isInit = true;
58+
LOGGER.info("SecurityHeadersFilter initialized with {} headers", headers.size());
59+
}
4260
}
4361

4462
@Override
45-
public void init(FilterConfig filterConfig) {
63+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
64+
throws IOException, ServletException {
65+
initSecurityHeaders();
66+
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
67+
headers.forEach(httpServletResponse::setHeader);
68+
chain.doFilter(request, response);
4669
}
4770

4871
@Override

roda-ui/roda-wui/src/main/resources/config/roda-wui.properties

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,44 @@ ui.filter.cas.serverName = https://localhost:8888
161161
ui.filter.cas.exceptionOnValidationFailure = false
162162
ui.filter.cas.redirectAfterValidation = false
163163

164+
##########################################################################
165+
# Security Headers filter settings
166+
#
167+
# These properties define which HTTP security headers should be applied
168+
# to all responses by the SecurityHeadersFilter. Each header can be
169+
# enabled by listing it in the array and its value can be configured
170+
# individually.
171+
#
172+
# Usage:
173+
#
174+
# * ui.filter.security-headers[] = <Header-Name>
175+
# Adds the header <Header-Name> to the list of headers managed by the filter.
176+
#
177+
# * ui.filter.security-headers[].<Header-Name> = <Header-Value>
178+
# Defines the value that will be sent for <Header-Name> in HTTP responses.
179+
#
180+
##########################################################################
181+
182+
ui.filter.security-headers[] = Strict-Transport-Security
183+
ui.filter.security-headers[] = Content-Security-Policy
184+
ui.filter.security-headers[] = X-XSS-Protection
185+
ui.filter.security-headers[] = X-Permitted-Cross-Domain-Policies
186+
ui.filter.security-headers[] = Feature-Policy
187+
ui.filter.security-headers[] = X-Frame-Options
188+
ui.filter.security-headers[] = X-Content-Type-Options
189+
ui.filter.security-headers[] = Referrer-Policy
190+
ui.filter.security-headers[] = Permissions-Policy
191+
192+
ui.filter.security-headers[].Strict-Transport-Security = max-age=31536000; includeSubDomains
193+
ui.filter.security-headers[].Content-Security-Policy = default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com https://www.google-analytics.com https://www.gstatic.com http://127.0.0.1:9876; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self';
194+
ui.filter.security-headers[].X-XSS-Protection = 1; mode=block
195+
ui.filter.security-headers[].X-Permitted-Cross-Domain-Policies = none
196+
ui.filter.security-headers[].Feature-Policy = camera 'none'; fullscreen 'self'; geolocation *; microphone 'self'
197+
ui.filter.security-headers[].X-Frame-Options = SAMEORIGIN
198+
ui.filter.security-headers[].X-Content-Type-Options = nosniff
199+
ui.filter.security-headers[].Referrer-Policy = no-referrer
200+
ui.filter.security-headers[].Permissions-Policy = geolocation=(self)
201+
164202
##########################################################################
165203
# Roles with access to menu items settings
166204
#

0 commit comments

Comments
 (0)