diff --git a/docker/Dockerfile b/docker/Dockerfile index f10220e376..6ffacc1b9e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -50,8 +50,9 @@ COPY ./docker-files/docker-entrypoint.sh / COPY ./docker-files/docker-entrypoint.d/* /docker-entrypoint.d/ RUN set -ex; \ - groupadd -r --gid "$RODA_GID" "$RODA_GROUP"; \ - useradd -r --uid "$RODA_UID" --gid "$RODA_GID" "$RODA_USER"; \ + usermod -l roda ubuntu; \ + usermod -d /home/roda -m roda; \ + groupmod -n roda ubuntu; \ mkdir -p -m0770 "$RODA_HOME/data"; \ mkdir -p -m0770 "$RODA_HOME/config"; \ chown -R "$RODA_USER:0" "$RODA_HOME" diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/RODA.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/RODA.java index 5baea64d51..48e1fbae8a 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/RODA.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/RODA.java @@ -7,13 +7,18 @@ */ package org.roda.wui; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; import org.apereo.cas.client.session.SingleSignOutHttpSessionListener; import org.roda.wui.filter.OnOffFilter; +import org.roda.wui.filter.SecurityHeadersFilter; import org.roda.wui.servlets.ContextListener; import org.roda.wui.servlets.RodaWuiServlet; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.web.servlet.ServletContextInitializer; import org.springframework.boot.web.servlet.ServletListenerRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; @@ -57,6 +62,8 @@ public FilterRegistrationBean internalWebAuthFilter() { return registrationBean; } + + @Bean public FilterRegistrationBean internalApiAuthFilter() { FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); @@ -192,6 +199,14 @@ public ServletRegistrationBean clientLogger() { return bean; } + @Bean + public FilterRegistrationBean securityHeadersFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new SecurityHeadersFilter()); + registrationBean.addUrlPatterns("/*"); // Apply the filter to all requests + return registrationBean; + } + @Bean public ServletRegistrationBean userManagementService() { ServletRegistrationBean bean; @@ -216,6 +231,18 @@ public ServletRegistrationBean browserService() { return bean; } + @Bean + public ServletContextInitializer servletContextInitializer() { + return new ServletContextInitializer() { + + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + servletContext.getSessionCookieConfig().setSecure(true); + servletContext.getSessionCookieConfig().setHttpOnly(true); + } + }; + } + @Configuration public static class DefaultView implements WebMvcConfigurer { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/filter/SecurityHeadersFilter.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/filter/SecurityHeadersFilter.java new file mode 100644 index 0000000000..e7c51fda93 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/filter/SecurityHeadersFilter.java @@ -0,0 +1,31 @@ +package org.roda.wui.filter; + +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class SecurityHeadersFilter implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + + httpServletResponse.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains"); + httpServletResponse.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; img-src 'self'; font-src 'self';"); + httpServletResponse.setHeader("X-Frame-Options", "SAMEORIGIN"); + httpServletResponse.setHeader("X-Content-Type-Options", "nosniff"); + httpServletResponse.setHeader("Referrer-Policy", "no-referrer"); + httpServletResponse.setHeader("Permissions-Policy", "geolocation=(self)"); + + chain.doFilter(request, response); + } + + @Override + public void init(FilterConfig filterConfig) { + } + + @Override + public void destroy() { + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/resources/config/theme/cookies/cookieconsent.js b/roda-ui/roda-wui/src/main/resources/config/theme/cookies/cookieconsent.js index f4a3500eaa..1441757983 100644 --- a/roda-ui/roda-wui/src/main/resources/config/theme/cookies/cookieconsent.js +++ b/roda-ui/roda-wui/src/main/resources/config/theme/cookies/cookieconsent.js @@ -93,16 +93,19 @@ return null; }, - setCookie: function (name, value, expiryDays, domain, path) { + setCookie: function (name, value, expiryDays, domain, path, sameSite) { expiryDays = expiryDays || 365; + sameSite = sameSite || 'Strict'; // Default SameSite value + var exdate = new Date(); exdate.setDate(exdate.getDate() + expiryDays); var cookie = [ name + '=' + value, 'expires=' + exdate.toUTCString(), - 'path=' + path || '/' + 'path=' + path || '/', + 'SameSite=' + sameSite // Correctly include SameSite attribute ]; if (domain) { @@ -337,7 +340,7 @@ }, setDismissedCookie: function () { - Util.setCookie(DISMISSED_COOKIE, 'yes', this.options.expiryDays, this.options.domain, this.options.path); + Util.setCookie(DISMISSED_COOKIE, 'yes', this.options.expiryDays, this.options.domain, this.options.path, this.options.sameSite); } }; diff --git a/roda-ui/roda-wui/src/main/webapp/META-INF/context.xml b/roda-ui/roda-wui/src/main/webapp/META-INF/context.xml new file mode 100644 index 0000000000..566e38d24e --- /dev/null +++ b/roda-ui/roda-wui/src/main/webapp/META-INF/context.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/webapp/WEB-INF/web.xml b/roda-ui/roda-wui/src/main/webapp/WEB-INF/web.xml index dee5232024..87594376c2 100644 --- a/roda-ui/roda-wui/src/main/webapp/WEB-INF/web.xml +++ b/roda-ui/roda-wui/src/main/webapp/WEB-INF/web.xml @@ -234,6 +234,15 @@ /logout + + SecurityHeadersFilter + org.roda.wui.filter.SecurityHeadersFilter + + + SecurityHeadersFilter + /* + + @@ -338,4 +347,14 @@ + + + + true + true + + + + +