Session IDs and auth codes should have 128 bits of entropy

Closes #42274

Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
This commit is contained in:
Alexander Schwartz 2025-09-11 17:05:40 +02:00 committed by GitHub
parent b6cee86e74
commit 6ea3c8aedf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 16 additions and 14 deletions

View File

@ -2,6 +2,7 @@ package org.keycloak.common.util;
import java.security.SecureRandom;
import java.util.Random;
import java.util.UUID;
public class SecretGenerator {
@ -113,4 +114,13 @@ public class SecretGenerator {
public static int equivalentEntropySize(int length, int srcAlphabetLength, int dstAlphabetLeng) {
return (int) Math.ceil(length * ((Math.log(srcAlphabetLength)) / (Math.log(dstAlphabetLeng))));
}
/**
* Returns a pseudo-UUID where all bits are random to have a maximum entropy.
*
* @return UUID with all bits random
*/
public UUID generateSecureUUID() {
return new UUID(random.get().nextLong(), random.get().nextLong());
}
}

View File

@ -27,8 +27,8 @@ import org.infinispan.affinity.KeyAffinityService;
import org.infinispan.affinity.KeyAffinityServiceFactory;
import org.infinispan.affinity.KeyGenerator;
import org.jboss.logging.Logger;
import org.keycloak.common.util.SecretGenerator;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.sessions.StickySessionEncoderProvider;
/**
@ -88,7 +88,7 @@ public class InfinispanKeyGenerator {
@Override
public UUID getKey() {
return UUID.randomUUID();
return SecretGenerator.getInstance().generateSecureUUID();
}
}
@ -97,7 +97,7 @@ public class InfinispanKeyGenerator {
@Override
public String getKey() {
return KeycloakModelUtils.generateId();
return SecretGenerator.getInstance().generateSecureID();
}
}
}

View File

@ -26,6 +26,7 @@ import org.keycloak.OAuthErrorException;
import org.keycloak.TokenIdGenerator;
import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.common.util.SecretGenerator;
import org.keycloak.common.util.Time;
import org.keycloak.connections.httpclient.HttpClientProvider;
import org.keycloak.constants.AdapterConstants;
@ -65,7 +66,6 @@ import org.keycloak.util.TokenUtil;
import java.io.IOException;
import java.net.URI;
import java.util.Optional;
import java.util.UUID;
import static org.keycloak.protocol.oidc.grants.device.DeviceGrantType.approveOAuth2DeviceAuthorization;
import static org.keycloak.protocol.oidc.grants.device.DeviceGrantType.denyOAuth2DeviceAuthorization;
@ -251,7 +251,7 @@ public class OIDCLoginProtocol implements LoginProtocol {
// Standard or hybrid flow
String code = null;
if (responseType.hasResponseType(OIDCResponseType.CODE)) {
OAuth2Code codeData = new OAuth2Code(UUID.randomUUID().toString(),
OAuth2Code codeData = new OAuth2Code(SecretGenerator.getInstance().generateSecureID(),
Time.currentTime() + userSession.getRealm().getAccessCodeLifespan(),
nonce,
authSession.getClientNote(OAuth2Constants.SCOPE),

View File

@ -82,21 +82,13 @@ public class OAuth2CodeParser {
return result.illegalCode();
}
String codeUUID = parsed[0];
String userSessionId = parsed[1];
String clientUUID = parsed[2];
event.detail(Details.CODE_ID, userSessionId);
event.session(userSessionId);
// Parse UUID
String codeUUID;
try {
codeUUID = parsed[0];
} catch (IllegalArgumentException re) {
logger.warn("Invalid format of the UUID in the code");
return result.illegalCode();
}
// Retrieve UserSession
var userSessionProvider = session.sessions();
UserSessionModel userSession = userSessionProvider.getUserSessionIfClientExists(realm, userSessionId, false, clientUUID);