diff --git a/CHANGELOG.md b/CHANGELOG.md index 389ec917bb2460c29e0b4fb7e54eac4d8aac63fd..824c9fcc4bd80477389ab28811c1405f06e663cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## 2.2.2 +This release fixes a security vulnerability caused by the CORS implementation. +Origins allowed for CORS can now be set in the configuration via +`security.cors.origins`. (Reported by Rainer Rillke at Wikimedia) + +Additional changes: +* Libraries have been upgraded to fix potential bugs + +## 2.1.2 +This release fixes a security vulnerability caused by the CORS implementation. +Support for cross-origin requests has been removed. Use ARSnova version 2.2 or +newer for proper CORS. (Reported by Rainer Rillke at Wikimedia) + +Additional changes: +* Libraries have been upgraded to fix potential bugs + +## 2.0.4 +This release fixes a security vulnerability caused by the CORS implementation. +Support for cross-origin requests has been removed. Use ARSnova version 2.2 or +newer for proper CORS. (Reported by Rainer Rillke at Wikimedia) + +Additional changes: +* Libraries have been upgraded to fix potential bugs + ## 2.3.2 This release fixes a security vulnerability in the account management API. It is highly recommended to upgrade if you are using database authentication. diff --git a/pom.xml b/pom.xml index 84579700e6317a3ccd1e39dac9ce383f3c511701..32a53802d2b0660dfb2aae5f3b39d6e5bcb60deb 100644 --- a/pom.xml +++ b/pom.xml @@ -325,7 +325,7 @@ <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> - <version>9.2.16.v20160414</version> + <version>9.2.17.v20160517</version> <configuration> <scanIntervalSeconds>1</scanIntervalSeconds> <webApp> diff --git a/src/main/java/de/thm/arsnova/config/ExtraConfig.java b/src/main/java/de/thm/arsnova/config/ExtraConfig.java index 36b3e2a0fc192610f91d4034c96ea447c6f5307b..e7f71f36818076b1139db5f00c382e964e956366 100644 --- a/src/main/java/de/thm/arsnova/config/ExtraConfig.java +++ b/src/main/java/de/thm/arsnova/config/ExtraConfig.java @@ -18,6 +18,7 @@ package de.thm.arsnova.config; import de.thm.arsnova.ImageUtils; +import de.thm.arsnova.web.CorsFilter; import de.thm.arsnova.connector.client.ConnectorClient; import de.thm.arsnova.connector.client.ConnectorClientImpl; import de.thm.arsnova.socket.ARSnovaSocket; @@ -40,6 +41,9 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import java.util.Arrays; +import java.util.Collections; + /** * Loads property file and configures non-security related beans and components. */ @@ -61,6 +65,7 @@ public class ExtraConfig extends WebMvcConfigurerAdapter { @Value(value = "${security.ssl}") private boolean socketUseSll; @Value(value = "${security.keystore}") private String socketKeystore; @Value(value = "${security.storepass}") private String socketStorepass; + @Value(value = "${security.cors.origins:}") private String[] corsOrigins; @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { @@ -82,6 +87,11 @@ public class ExtraConfig extends WebMvcConfigurerAdapter { return propertiesFactoryBean; } + @Bean + public CorsFilter corsFilter() { + return new CorsFilter(Arrays.asList(corsOrigins)); + } + @Bean(name = "connectorClient") public ConnectorClient connectorClient() { if (!connectorEnable) { diff --git a/src/main/java/de/thm/arsnova/web/CorsFilter.java b/src/main/java/de/thm/arsnova/web/CorsFilter.java index f4c434e02cc65f0a89da48cdb9acda407780cdd1..acd2f7dc1541bbee46c504e0a90d2a5d9065e3de 100644 --- a/src/main/java/de/thm/arsnova/web/CorsFilter.java +++ b/src/main/java/de/thm/arsnova/web/CorsFilter.java @@ -17,36 +17,52 @@ */ package de.thm.arsnova.web; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; +import java.util.List; -/** - * Sets response headers to allow CORS requests. - */ -@Component -public class CorsFilter extends OncePerRequestFilter { +public class CorsFilter extends org.springframework.web.filter.CorsFilter { + protected final Logger logger = LoggerFactory.getLogger(CorsFilter.class); + + public CorsFilter(List<String> origins) { + super(configurationSource(origins)); + logger.info("CorsFilter initialized. Allowed origins: {}", origins); + } - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - response.addHeader("Access-Control-Allow-Credentials", "true"); - response.addHeader("Access-Control-Allow-Methods", "GET"); - response.addHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With"); + private static UrlBasedCorsConfigurationSource configurationSource(List<String> origins) { + CorsConfiguration config; + UrlBasedCorsConfigurationSource source; - if (request.getHeader("origin") != null) { - response.addHeader("Access-Control-Allow-Origin", sanitizeOriginUrl(request.getHeader("origin"))); - } + /* Grant full access from specified origins */ + config = new CorsConfiguration(); + config.setAllowedOrigins(origins); + config.addAllowedHeader("Accept"); + config.addAllowedHeader("Content-Type"); + config.addAllowedHeader("X-Requested-With"); + config.addAllowedMethod("GET"); + config.addAllowedMethod("POST"); + config.addAllowedMethod("PUT"); + config.addAllowedMethod("DELETE"); + config.setAllowCredentials(true); + source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); - filterChain.doFilter(request, response); - } + /* Grant limited access from all origins */ + config = new CorsConfiguration(); + config.addAllowedOrigin("*"); + config.addAllowedHeader("Accept"); + config.addAllowedHeader("X-Requested-With"); + config.addAllowedMethod("GET"); + config.setAllowCredentials(true); + source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/", config); + source.registerCorsConfiguration("/arsnova-config", config); + source.registerCorsConfiguration("/configuration/", config); + source.registerCorsConfiguration("/statistics", config); - private String sanitizeOriginUrl(String originUrl) { - return originUrl.replaceAll("[\n\r]+", " "); + return source; } } diff --git a/src/main/resources/arsnova.properties.example b/src/main/resources/arsnova.properties.example index b81d3369d0c51d9160b23fffccba1e9b21882ee6..cd63c90818bd45d4af4510362910bb4b70e930da 100644 --- a/src/main/resources/arsnova.properties.example +++ b/src/main/resources/arsnova.properties.example @@ -160,6 +160,15 @@ security.google.key= security.google.secret= +################################################################################ +# Cross-Origin Resource Sharing +################################################################################ +# CORS grants full API access to client-side (browser) applications from other +# domains. Multiple entries are separated by commas. Untrusted and vulnerable +# applications running on these domains pose a security risk to ARSnova users. +#security.cors.origins=https:// + + ################################################################################ # ARSnova Connector (for LMS) ################################################################################ diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 7daa0e4c21695641e954b8e1b98c8239c3c9e33a..95f404bd0d1c9ec724d76b00eb5aba0bff6f4818 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -63,7 +63,7 @@ <filter> <filter-name>corsFilter</filter-name> - <filter-class>de.thm.arsnova.web.CorsFilter</filter-class> + <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <async-supported>true</async-supported> </filter> <filter-mapping> diff --git a/src/test/resources/arsnova.properties.example b/src/test/resources/arsnova.properties.example index b81d3369d0c51d9160b23fffccba1e9b21882ee6..cd63c90818bd45d4af4510362910bb4b70e930da 100644 --- a/src/test/resources/arsnova.properties.example +++ b/src/test/resources/arsnova.properties.example @@ -160,6 +160,15 @@ security.google.key= security.google.secret= +################################################################################ +# Cross-Origin Resource Sharing +################################################################################ +# CORS grants full API access to client-side (browser) applications from other +# domains. Multiple entries are separated by commas. Untrusted and vulnerable +# applications running on these domains pose a security risk to ARSnova users. +#security.cors.origins=https:// + + ################################################################################ # ARSnova Connector (for LMS) ################################################################################