diff --git a/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/service/session/CBSessionManager.java b/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/service/session/CBSessionManager.java index 3f7cc846836..28885879407 100644 --- a/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/service/session/CBSessionManager.java +++ b/server/bundles/io.cloudbeaver.server.ce/src/io/cloudbeaver/service/session/CBSessionManager.java @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2025 DBeaver Corp and others + * Copyright (C) 2010-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -199,6 +199,26 @@ protected String getSessionId(@NotNull HttpServletRequest request) { return httpSession.getId(); } + /** + * Rotates the session ID after successful authentication to prevent session fixation attacks. + */ + public void rotateSessionId(@NotNull HttpServletRequest request) { + HttpSession oldSession = request.getSession(false); + if (oldSession == null) { + log.debug("No HTTP session present, skipping session ID rotation"); + return; + } + String oldSessionId = oldSession.getId(); + String newSessionId = request.changeSessionId(); + synchronized (sessionMap) { + BaseWebSession webSession = sessionMap.remove(oldSessionId); + if (webSession != null) { + sessionMap.put(newSessionId, webSession); + } + } + log.debug("Session ID rotated after authentication ('" + oldSessionId + "' -> '" + newSessionId + "')"); + } + /** * Returns not expired session from cache, or restore it. * diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java index a5c5371af20..4dc27421ca6 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/DBWServiceAuth.java @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2025 DBeaver Corp and others + * Copyright (C) 2010-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ public interface DBWServiceAuth extends DBWService { @WebAction(authRequired = false) WebAuthStatus authLogin( + @NotNull HttpServletRequest httpRequest, @NotNull WebSession webSession, @NotNull String providerId, @Nullable String providerConfigurationId, @@ -56,6 +57,7 @@ WebAsyncAuthStatus federatedLogin( @WebAction(authRequired = false) WebAsyncAuthTaskResult federatedAuthTaskResult( + @NotNull HttpServletRequest httpRequest, @NotNull WebSession webSession, @NotNull String taskId ) throws DBWebException; diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebAsyncAuthJob.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebAsyncAuthJob.java index 00105a7daa3..4d0d392f010 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebAsyncAuthJob.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebAsyncAuthJob.java @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2025 DBeaver Corp and others + * Copyright (C) 2010-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ public class WebAsyncAuthJob extends AbstractCancelableJob { //to get auth result @Nullable private List authResult; + private boolean sessionRotated; public WebAsyncAuthJob(@NotNull String name, @NotNull String authId, boolean linkWithUser) { super(name); @@ -46,7 +47,6 @@ public WebAsyncAuthJob(@NotNull String name, @NotNull String authId, boolean lin protected IStatus run(@NotNull DBRProgressMonitor monitor) { return null; } - @NotNull public String getAuthId() { return authId; @@ -65,4 +65,12 @@ public void setAuthResult(@Nullable List authResult) { this.authResult = authResult; } + public boolean isSessionRotated() { + return sessionRotated; + } + + public void setSessionRotated(boolean sessionRotated) { + this.sessionRotated = sessionRotated; + } + } diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java index e788df0c5fc..d133ea9c92e 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/WebServiceBindingAuth.java @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2025 DBeaver Corp and others + * Copyright (C) 2010-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ public WebServiceBindingAuth() { public void bindWiring(DBWBindingContext model) { model.getQueryType() .dataFetcher("authLogin", env -> getService(env).authLogin( + GraphQLEndpoint.getServletRequestOrThrow(env), getWebSession(env, false), getArgumentVal(env, "provider"), getArgument(env, "configuration"), @@ -45,6 +46,7 @@ public void bindWiring(DBWBindingContext model) { CommonUtils.toBoolean(getArgument(env, "forceSessionsLogout")) )) .dataFetcher("federatedAuthTaskResult", env -> getService(env).federatedAuthTaskResult( + GraphQLEndpoint.getServletRequestOrThrow(env), getWebSession(env, false), getArgumentVal(env, "taskId") )) diff --git a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java index 7df05d3c467..aa83122cfad 100644 --- a/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java +++ b/server/bundles/io.cloudbeaver.service.auth/src/io/cloudbeaver/service/auth/impl/WebServiceAuthImpl.java @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2025 DBeaver Corp and others + * Copyright (C) 2010-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,6 +67,7 @@ public class WebServiceAuthImpl implements DBWServiceAuth { @Override public WebAuthStatus authLogin( + @NotNull HttpServletRequest httpRequest, @NotNull WebSession webSession, @NotNull String providerId, @Nullable String providerConfigurationId, @@ -85,7 +86,9 @@ public WebAuthStatus authLogin( } else { //run it sync var authProcessor = new WebSessionAuthProcessor(webSession, smAuthInfo, linkWithActiveUser); - return new WebAuthStatus(smAuthInfo.getAuthStatus(), authProcessor.authenticateSession()); + List authInfos = authProcessor.authenticateSession(); + CBApplication.getInstance().getSessionManager().rotateSessionId(httpRequest); + return new WebAuthStatus(smAuthInfo.getAuthStatus(), authInfos); } } catch (SMTooManySessionsException e) { throw new DBWebException("User authentication failed", e.getErrorType(), e); @@ -139,7 +142,11 @@ public WebAsyncAuthStatus federatedLogin( } @Override - public WebAsyncAuthTaskResult federatedAuthTaskResult(@NotNull WebSession webSession, @NotNull String taskId) throws DBWebException { + public WebAsyncAuthTaskResult federatedAuthTaskResult( + @NotNull HttpServletRequest httpRequest, + @NotNull WebSession webSession, + @NotNull String taskId + ) throws DBWebException { WebAsyncTaskInfo taskInfo = webSession.asyncTaskStatus(taskId, true); if (taskInfo == null) { throw new DBWebException("Task '" + taskId + "' not found"); @@ -154,6 +161,9 @@ public WebAsyncAuthTaskResult federatedAuthTaskResult(@NotNull WebSession webSes List userTokens = job.getAuthResult(); if (CommonUtils.isEmpty(userTokens)) { userTokens = List.of(); + } else if (!job.isSessionRotated()) { + CBApplication.getInstance().getSessionManager().rotateSessionId(httpRequest); + job.setSessionRotated(true); } return new WebAsyncAuthTaskResult(userTokens); } @@ -261,6 +271,7 @@ public WebLogoutInfo authLogout( } } } + CBApplication.getInstance().getSessionManager().rotateSessionId(httpRequest); return new WebLogoutInfo(logoutUrls); } catch (DBException e) { throw new DBWebException("User logout failed", e);