CVE-2020-28191 (published 2022-12-26T22:15Z)

The console in Togglz before 2.9.4 allows CSRF.

CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-352
cwe_name: Cross-Site Request Forgery (CSRF)
description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
extended_description: When a web server is designed to receive a request from a client without any mechanism for verifying that it was intentionally sent, then it might be possible for an attacker to trick a client into making an unintentional request to the web server which will be treated as an authentic request. This can be done via a URL, image load, XMLHttpRequest, etc. and can result in exposure of data or unintended code execution.
         

Fix information:
repo_url: https://github.com/togglz/togglz
commit_details: repo_url: https://github.com/togglz/togglz
author: Joe Beeton
author_date: 2021-01-12 07:19:14+00:00
msg: Added CSRF protection to the togglz console via a CSRF token passed between the server and the clinet. This remediates the vulnerabilty CVE-2020-28191 by blocking CSRF attacks as the attcker will not be able to guess the CSRF token value. (#495)

This has been implemented with either the session timeout of the application the togglz console is embedded in. Or if no user session is available it defaults to a 10 minute timeout for the CSRF token.
This CSRF token does not interfere with either OWASP's CSRFGuard or Spring-Security's CSRF protection if they are used within the application.

Co-authored-by: Joseph Beeton <joseph.p.beeton1@aexp.com>
repository: repo_url: https://github.com/togglz/togglz
repo_name: togglz/togglz
description: Feature Flags for the Java platform
repo_language: Java
owner: togglz
file_changes: filename: pom.xml
change_type: ModificationType.MODIFY
diff: @@ -77,5 +77,11 @@
       <version>6.0.0</version>
     </dependency>
 
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-collections4</artifactId>
+      <version>4.4</version>
+    </dependency>
+
   </dependencies>
 </project>

programming_language: Markdown
filename: EditPageHandler.java
change_type: ModificationType.MODIFY
diff: @@ -19,6 +19,7 @@ import org.togglz.core.spi.ActivationStrategy;
 
 import com.floreysoft.jmte.Engine;
 import org.togglz.core.util.Services;
+import org.togglz.servlet.spi.CSRFTokenValidator;
 
 public class EditPageHandler extends RequestHandlerBase {
 
@@ -37,7 +38,10 @@ public class EditPageHandler extends RequestHandlerBase {
         FeatureManager featureManager = event.getFeatureManager();
         HttpServletRequest request = event.getRequest();
         HttpServletResponse response = event.getResponse();
-
+		if(!validateCSRFToken(event)) {
+			renderErrorPage(event);
+			return;
+		}
         // identify the feature
         Feature feature = null;
         String featureAsString = request.getParameter("f");
@@ -78,7 +82,6 @@ public class EditPageHandler extends RequestHandlerBase {
                 response.sendRedirect("index");
 
             }
-
             // got validation errors
             else {
                 renderEditPage(event, featureModel);
@@ -88,6 +91,24 @@ public class EditPageHandler extends RequestHandlerBase {
 
     }
 
+    private boolean validateCSRFToken(RequestEvent event) {
+    	boolean isValid = true;
+		for (CSRFTokenValidator validator : Services.get(CSRFTokenValidator.class)) {
+			if(!validator.isTokenValid(event.getRequest())){
+				isValid = false;
+				break;
+			}
+		}
+		return isValid;
+	}
+
+    private void renderErrorPage(RequestEvent event) throws IOException {
+		String template = getResourceAsString("error.html");
+		String content = new Engine().transform(template, new HashMap<>());
+		event.getResponse().setStatus(401);
+		writeResponse(event, content);
+	}
+
     private void renderEditPage(RequestEvent event, FeatureModel featureModel) throws IOException {
         List<CSRFToken> tokens = new ArrayList<>();
         for (CSRFTokenProvider provider : Services.get(CSRFTokenProvider.class)) {
@@ -96,7 +117,6 @@ public class EditPageHandler extends RequestHandlerBase {
                 tokens.add(token);
             }
         }
-
         Map<String, Object> model = new HashMap<>();
         model.put("model", featureModel);
         model.put("tokens", tokens);

programming_language: Java
filename: TogglzCSRFTokenCache.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,32 @@
+package org.togglz.console.security;
+
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.collections4.map.PassiveExpiringMap;
+import org.togglz.servlet.spi.CSRFToken;
+
+public class TogglzCSRFTokenCache {
+
+	private static final PassiveExpiringMap<String, CSRFToken> expiringMap;
+	private static final Object lock = new Object();
+	static {
+		PassiveExpiringMap.ConstantTimeToLiveExpirationPolicy<String, CSRFToken>
+				expirationPolicy = new PassiveExpiringMap.ConstantTimeToLiveExpirationPolicy<>(
+				10, TimeUnit.MINUTES);
+		expiringMap = new PassiveExpiringMap<>(expirationPolicy, new HashMap<>());
+	}
+
+	public static void cacheToken(CSRFToken token) {
+		synchronized (lock) {
+			expiringMap.put(token.getValue(), token);
+		}
+	}
+
+	public static boolean isTokenInCache(CSRFToken token) {
+		synchronized (lock) {
+			return expiringMap.containsKey(token.getValue());
+		}
+	}
+	
+}
\ No newline at end of file

programming_language: Java
filename: TogglzCSRFTokenProvider.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,25 @@
+package org.togglz.console.security;
+
+import java.util.UUID;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.togglz.servlet.spi.CSRFToken;
+import org.togglz.servlet.spi.CSRFTokenProvider;
+
+import static org.togglz.console.security.TogglzCSRFTokenValidator.CSRF_TOKEN_NAME;
+
+public class TogglzCSRFTokenProvider implements CSRFTokenProvider {
+
+	@Override
+	public CSRFToken getToken(HttpServletRequest request) {
+		CSRFToken token;
+		if (request.getAttribute(CSRF_TOKEN_NAME) == null) {
+			token = new CSRFToken(CSRF_TOKEN_NAME, UUID.randomUUID().toString());
+			TogglzCSRFTokenCache.cacheToken(token);
+		} else {
+			token = new CSRFToken(CSRF_TOKEN_NAME, request.getAttribute(CSRF_TOKEN_NAME).toString());
+		}
+		return token;
+	}
+}
\ No newline at end of file

programming_language: Java
filename: TogglzCSRFTokenValidator.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,22 @@
+package org.togglz.console.security;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.togglz.servlet.spi.CSRFToken;
+import org.togglz.servlet.spi.CSRFTokenValidator;
+
+public class TogglzCSRFTokenValidator implements CSRFTokenValidator {
+
+
+	public static final String CSRF_TOKEN_NAME = "togglz_csrf";
+
+	@Override
+	public boolean isTokenValid(HttpServletRequest request) {
+		String token = request.getParameter(CSRF_TOKEN_NAME);
+		if(token==null) {
+			return false;
+		} else {
+			return TogglzCSRFTokenCache.isTokenInCache(new CSRFToken(CSRF_TOKEN_NAME,token));
+		}
+	}
+}

programming_language: Java
filename: org.togglz.servlet.spi.CSRFTokenProvider
change_type: ModificationType.ADD
diff: @@ -0,0 +1 @@
+org.togglz.console.security.TogglzCSRFTokenProvider
\ No newline at end of file

programming_language: Markdown
filename: org.togglz.servlet.spi.CSRFTokenValidator
change_type: ModificationType.ADD
diff: @@ -0,0 +1 @@
+org.togglz.console.security.TogglzCSRFTokenValidator
\ No newline at end of file

programming_language: Markdown
filename: error.html
change_type: ModificationType.ADD
diff: @@ -0,0 +1,4 @@
+<div style="text-align: center;">
+    <h1>ERROR</h1>
+    <h1><small>Invalid CSRF Token, please refresh browser from the main Togglz page.</small></h1>
+</div>
\ No newline at end of file

programming_language: SQL
filename: index.html
change_type: ModificationType.MODIFY
diff: @@ -67,8 +67,7 @@
                 ${end}
               </td>
               <td class="feature-actions">
-                <a class="btn btn-sm btn-default" href="edit?f=${feature.name}" title="Edit ${feature.label} feature">
-                  <span class="glyphicon glyphicon-cog text-muted"></span>
+                  <a class="btn btn-sm btn-default" href="edit?f=${feature.name}${foreach tokens token}${if token.name = "togglz_csrf"}&${token.name}=${token.value}${end}${end}" title="Edit ${feature.label} feature">                  <span class="glyphicon glyphicon-cog text-muted"></span>
                 </a>
               </td>
             </tr>

programming_language: HTML
filename: CSRFTokenValidator.java
change_type: ModificationType.ADD
diff: @@ -0,0 +1,9 @@
+package org.togglz.servlet.spi;
+
+import javax.servlet.http.HttpServletRequest;
+
+public interface CSRFTokenValidator {
+
+
+	boolean isTokenValid(HttpServletRequest request);
+}

programming_language: Java