VYPR
Medium severity6.5NVD Advisory· Published Jan 14, 2025· Updated Apr 15, 2026

CVE-2024-11734

CVE-2024-11734

Description

A denial of service vulnerability was found in Keycloak that could allow an administrative user with the right to change realm settings to disrupt the service. This action is done by modifying any of the security headers and inserting newlines, which causes the Keycloak server to write to a request that has already been terminated, leading to the failure of said request.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.keycloak:keycloak-quarkus-serverMaven
< 26.0.826.0.8

Patches

1
93b2a7327b25

EMBARGOED CVE-2024-11734 org.keycloak/keycloak-quarkus-server: Denial of Service in Keycloak Server via Security Headers (#228)

https://github.com/keycloak/keycloakDouglas PalmerDec 13, 2024via ghsa
4 files changed · +49 19
  • server-spi-private/src/main/java/org/keycloak/utils/ReservedCharValidator.java+23 17 modified
    @@ -19,6 +19,7 @@
     import jakarta.ws.rs.BadRequestException;
     import org.jboss.logging.Logger;
     
    +import java.util.Map;
     import java.util.regex.Matcher;
     import java.util.regex.Pattern;
     
    @@ -29,7 +30,7 @@
      */
     public class ReservedCharValidator {
         protected static final Logger logger = Logger.getLogger(ReservedCharValidator.class);
    -    
    +
         // https://tools.ietf.org/html/rfc3986#section-2.2
         private static final Pattern RESERVED_CHARS_PATTERN = Pattern.compile("[:/?#@!$&()*+,;=\\[\\]\\\\]");
     
    @@ -38,32 +39,28 @@ public class ReservedCharValidator {
     
         private ReservedCharValidator() {}
     
    -    public static void validate(String str, Pattern pattern) throws ReservedCharException {
    +    public static void validate(String str, Pattern pattern) {
    +        validate(str, pattern, null);
    +    }
    +
    +    public static void validate(String str, Pattern pattern, String message) throws ReservedCharException {
             if (str == null) return;
     
             Matcher matcher = pattern.matcher(str);
             if (matcher.find()) {
    -            String message = "Character '" + matcher.group() + "' not allowed.";
    +            if(message == null) {
    +                message = "Character '" + matcher.group() + "' not allowed.";
    +            }
                 logger.warn(message);
                 throw new ReservedCharException(message);
             }
         }
    -    
    +
         public static void validateNoSpace(String str) {
    -        if (str == null) return;
    -        
    -        Pattern pattern = Pattern.compile("\\s");
    -        Matcher matcher = pattern.matcher(str);
    -        
    -        if (matcher.find()) {
    -            String message = "Empty Space not allowed.";
    -            logger.warn(message);
    -            throw new ReservedCharException(message);
    -        }
    -        
    +        validate(str, Pattern.compile("\\s"), "Empty Space not allowed.");
             validate(str, RESERVED_CHARS_PATTERN);
         }
    -    
    +
         public static void validate(String str) {
             validate(str, RESERVED_CHARS_PATTERN);
         }
    @@ -75,7 +72,16 @@ public static void validateLocales(Iterable<String> strIterable) {
                 validate(str, RESERVED_CHARS_LOCALES_PATTERN);
             }
         }
    -    
    +
    +    public static void validateSecurityHeaders(Map<String, String> headers) {
    +        if (headers == null) return;
    +
    +        for (Map.Entry<String, String> entry : headers.entrySet()) {
    +            validate(entry.getKey(), Pattern.compile("\\n"), "Newline not allowed.");
    +            validate(entry.getValue(), Pattern.compile("\\n"), "Newline not allowed.");
    +        }
    +    }
    +
         public static class ReservedCharException extends BadRequestException {
             ReservedCharException(String msg) {
                 super(msg);
    
  • services/src/main/java/org/keycloak/services/managers/RealmManager.java+1 0 modified
    @@ -536,6 +536,7 @@ public RealmModel importRealm(RealmRepresentation rep, boolean skipUserDependent
                 session.getContext().setRealm(realm);
                 ReservedCharValidator.validate(rep.getRealm());
                 ReservedCharValidator.validateLocales(rep.getSupportedLocales());
    +            ReservedCharValidator.validateSecurityHeaders(rep.getBrowserSecurityHeaders());
                 realm.setName(rep.getRealm());
     
                 // setup defaults
    
  • services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java+8 2 modified
    @@ -418,8 +418,14 @@ public Response updateRealm(final RealmRepresentation rep) {
                 throw ErrorResponse.error("Can't rename master realm", Status.BAD_REQUEST);
             }
     
    -        ReservedCharValidator.validate(rep.getRealm());
    -        ReservedCharValidator.validateLocales(rep.getSupportedLocales());
    +        try {
    +            ReservedCharValidator.validate(rep.getRealm());
    +            ReservedCharValidator.validateLocales(rep.getSupportedLocales());
    +            ReservedCharValidator.validateSecurityHeaders(rep.getBrowserSecurityHeaders());
    +        } catch (ReservedCharValidator.ReservedCharException e) {
    +            logger.error(e.getMessage(), e);
    +            throw ErrorResponse.error(e.getMessage(), Status.BAD_REQUEST);
    +        }
     
             try {
                 if (!Constants.GENERATE.equals(rep.getPublicKey()) && (rep.getPrivateKey() != null && rep.getPublicKey() != null)) {
    
  • testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LoginStatusIframeEndpointTest.java+17 0 modified
    @@ -17,6 +17,7 @@
     
     package org.keycloak.testsuite.oauth;
     
    +import jakarta.ws.rs.BadRequestException;
     import org.apache.commons.io.IOUtils;
     import org.apache.http.Header;
     import org.apache.http.NameValuePair;
    @@ -35,6 +36,7 @@
     import org.keycloak.models.BrowserSecurityHeaders;
     import org.keycloak.models.Constants;
     import org.keycloak.representations.idm.ClientRepresentation;
    +import org.keycloak.representations.idm.ErrorRepresentation;
     import org.keycloak.representations.idm.RealmRepresentation;
     import org.keycloak.testsuite.AbstractKeycloakTest;
     import org.keycloak.testsuite.ActionURIUtils;
    @@ -57,6 +59,7 @@
     import static org.junit.Assert.assertNotNull;
     import static org.junit.Assert.assertNull;
     import static org.junit.Assert.assertTrue;
    +import static org.junit.Assert.fail;
     
     /**
      * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
    @@ -219,6 +222,20 @@ public void checkEmptyCsp() throws Exception {
             }
         }
     
    +    @Test
    +    public void checkCspWithNewline() throws Exception {
    +        try {
    +            new RealmAttributeUpdater(adminClient.realm("test"))
    +                    .setBrowserSecurityHeader(BrowserSecurityHeaders.CONTENT_SECURITY_POLICY.getKey(), "test\ntest")
    +                    .update();
    +            fail("Validation should fail due to newline");
    +        }
    +        catch (BadRequestException ex) {
    +            ErrorRepresentation errorRep = ex.getResponse().readEntity(ErrorRepresentation.class);
    +            assertEquals("Newline not allowed.", errorRep.getErrorMessage());
    +        }
    +    }
    +
         @Override
         public void addTestRealms(List<RealmRepresentation> testRealms) {
             testRealms.add(RealmBuilder.create().name("test").build());
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.