From 69efff10bd0dfe3ea947f3d519600c7046916dc8 Mon Sep 17 00:00:00 2001 From: Rajitha Kumara Date: Mon, 25 Sep 2023 09:51:11 +0000 Subject: [PATCH] Implement state checking and csrf protection ## Purpose * Fixes for http://roadmap.entgra.net/issues/9846 Co-authored-by: rajitha Reviewed-on: https://repository.entgra.net/community/device-mgt-core/pulls/232 Co-authored-by: Rajitha Kumara Co-committed-by: Rajitha Kumara --- .../request/interceptor/SsoLoginCallbackHandler.java | 10 +++++++++- .../core/ui/request/interceptor/SsoLoginHandler.java | 12 +++++++----- .../ui/request/interceptor/util/HandlerUtil.java | 6 ++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/SsoLoginCallbackHandler.java b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/SsoLoginCallbackHandler.java index 9e5c806723..2730090125 100644 --- a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/SsoLoginCallbackHandler.java +++ b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/SsoLoginCallbackHandler.java @@ -27,6 +27,7 @@ import io.entgra.device.mgt.core.ui.request.interceptor.util.HandlerUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpHeaders; +import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; @@ -39,6 +40,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; +import java.util.Objects; @MultipartConfig @WebServlet("/ssoLoginCallback") @@ -47,6 +49,7 @@ public class SsoLoginCallbackHandler extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + String state = req.getParameter("state"); String code = req.getParameter("code"); HttpSession session = req.getSession(false); @@ -66,6 +69,11 @@ public class SsoLoginCallbackHandler extends HttpServlet { return; } + if (state == null || !Objects.equals(state, session.getAttribute("state").toString())) { + resp.sendError(HttpStatus.SC_BAD_REQUEST, "MismatchingStateError: CSRF Warning! State not equal in request and response"); + return; + } + String scope = session.getAttribute("scope").toString(); HttpPost tokenEndpoint = new HttpPost(keyManagerUrl + HandlerConstants.OAUTH2_TOKEN_ENDPOINT); @@ -75,7 +83,7 @@ public class SsoLoginCallbackHandler extends HttpServlet { String loginCallbackUrl = iotsCoreUrl + req.getContextPath() + HandlerConstants.SSO_LOGIN_CALLBACK; StringEntity tokenEPPayload = new StringEntity( - "grant_type=" + HandlerConstants.CODE_GRANT_TYPE + "&code=" + code + "&state=&scope=" + scope + + "grant_type=" + HandlerConstants.CODE_GRANT_TYPE + "&code=" + code + "&scope=" + scope + "&redirect_uri=" + loginCallbackUrl, ContentType.APPLICATION_FORM_URLENCODED); tokenEndpoint.setEntity(tokenEPPayload); diff --git a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/SsoLoginHandler.java b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/SsoLoginHandler.java index 4686a8d0d7..78fb91f7ae 100644 --- a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/SsoLoginHandler.java +++ b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/SsoLoginHandler.java @@ -86,6 +86,7 @@ public class SsoLoginHandler extends HttpServlet { private LoginCache loginCache; private OAuthApp oAuthApp; private OAuthAppCacheKey oAuthAppCacheKey; + private String state; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) { @@ -97,6 +98,7 @@ public class SsoLoginHandler extends HttpServlet { httpSession = req.getSession(true); + state = HandlerUtil.generateStateToken(); initializeAdminCredentials(); baseContextPath = req.getContextPath(); applicationName = baseContextPath.substring(1, baseContextPath.indexOf("-ui-request-handler")); @@ -127,12 +129,11 @@ public class SsoLoginHandler extends HttpServlet { String scopesSsoString = HandlerUtil.getScopeString(scopesSsoJson); String loginCallbackUrl = iotsCoreUrl + baseContextPath + HandlerConstants.SSO_LOGIN_CALLBACK; persistAuthSessionData(req, oAuthApp.getClientId(), oAuthApp.getClientSecret(), - oAuthApp.getEncodedClientApp(), scopesSsoString); - + oAuthApp.getEncodedClientApp(), scopesSsoString, state); resp.sendRedirect(keyManagerUrl + HandlerConstants.AUTHORIZATION_ENDPOINT + "?response_type=code" + + "&state=" + state + "&client_id=" + clientId + - "&state=" + "&scope=openid " + scopesSsoString + "&redirect_uri=" + loginCallbackUrl); } catch (IOException e) { @@ -186,7 +187,7 @@ public class SsoLoginHandler extends HttpServlet { clientSecret = jClientAppResultAsJsonObject.get("client_secret").getAsString(); encodedClientApp = Base64.getEncoder().encodeToString((clientId + ":" + clientSecret).getBytes()); String scopesString = HandlerUtil.getScopeString(scopes); - persistAuthSessionData(req, clientId, clientSecret, encodedClientApp, scopesString); + persistAuthSessionData(req, clientId, clientSecret, encodedClientApp, scopesString, state); } // cache the oauth app credentials @@ -287,13 +288,14 @@ public class SsoLoginHandler extends HttpServlet { * @param scopes - User scopes */ private void persistAuthSessionData(HttpServletRequest req, String clientId, String clientSecret, - String encodedClientApp, String scopes) { + String encodedClientApp, String scopes, String state) { httpSession = req.getSession(false); httpSession.setAttribute("clientId", clientId); httpSession.setAttribute("clientSecret", clientSecret); httpSession.setAttribute("encodedClientApp", encodedClientApp); httpSession.setAttribute("scope", scopes); httpSession.setAttribute("redirectUrl", req.getParameter("redirect")); + httpSession.setAttribute("state", state); httpSession.setMaxInactiveInterval(sessionTimeOut); } diff --git a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/util/HandlerUtil.java b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/util/HandlerUtil.java index 75b1643598..0fca3811c0 100644 --- a/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/util/HandlerUtil.java +++ b/components/ui-request-interceptor/io.entgra.device.mgt.core.ui.request.interceptor/src/main/java/io/entgra/device/mgt/core/ui/request/interceptor/util/HandlerUtil.java @@ -71,6 +71,8 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.StringWriter; +import java.math.BigInteger; +import java.security.SecureRandom; import java.util.Enumeration; import java.util.List; @@ -763,4 +765,8 @@ public class HandlerUtil { } return otpManagementService; } + + public static String generateStateToken() { + return new BigInteger(130, new SecureRandom()).toString(32); + } }