[SIDP-214] - Installer needs to put (at least) bcprov onto the calsspath before it runs ant
[SIDP-222] - Template engine used by LDAP and database connectors throw an NPE on startup
[SIDP-224] - Add version information in library JAR manifest and provide command line tool to view it
+[SIDP-225] - Credential theft vulnerability in login.jsp
[SIDP-226] - Cross site scripting vulnerability
\ No newline at end of file
package edu.internet2.middleware.shibboleth.idp.authn;
import java.io.IOException;
+import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Set;
import java.util.Map.Entry;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
import javax.security.auth.Subject;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import org.opensaml.saml2.core.AuthnContext;
import org.opensaml.util.storage.ExpiringObject;
import org.opensaml.util.storage.StorageService;
+import org.opensaml.xml.util.Base64;
import org.opensaml.xml.util.DatatypeHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
HttpServletResponse httpResponse) {
LOG.debug("Returning control to profile handler at: {}", loginContext.getProfileHandlerURL());
httpRequest.setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginContext);
+
+ // Cleanup this cookie
+ Cookie lcKeyCookie = new Cookie(LOGIN_CONTEXT_KEY_NAME, "");
+ lcKeyCookie.setMaxAge(0);
+ httpResponse.addCookie(lcKeyCookie);
+
forwardRequest(loginContext.getProfileHandlerURL(), httpRequest, httpResponse);
}
Session userSession) {
httpRequest.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, userSession);
+ String remoteAddress = httpRequest.getRemoteAddr();
+ String sessionId = userSession.getSessionID();
+
+ String signature = null;
+ SecretKey signingKey = userSession.getSessionSecretKey();
+ try {
+ Mac mac = Mac.getInstance("HmacSHA256");
+ mac.init(signingKey);
+ mac.update(remoteAddress.getBytes());
+ mac.update(sessionId.getBytes());
+ signature = Base64.encodeBytes(mac.doFinal());
+ } catch (GeneralSecurityException e) {
+ LOG.error("Unable to compute signature over session cookie material", e);
+ }
+
LOG.debug("Adding IdP session cookie to HTTP response");
- Cookie sessionCookie = new Cookie(IDP_SESSION_COOKIE_NAME, userSession.getSessionID());
+ Cookie sessionCookie = new Cookie(IDP_SESSION_COOKIE_NAME, remoteAddress + "|" + sessionId + "|" + signature);
String contextPath = httpRequest.getContextPath();
if (DatatypeHelper.isEmpty(contextPath)) {
package edu.internet2.middleware.shibboleth.idp.session;
import java.io.IOException;
+import java.security.GeneralSecurityException;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import org.joda.time.DateTime;
+import org.opensaml.xml.util.Base64;
import org.opensaml.xml.util.DatatypeHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Class Logger. */
private final Logger log = LoggerFactory.getLogger(IdPSessionFilter.class);
+ /** Whether the client must always come back from the same address. */
+ private boolean consistentAddress;
+
/** IdP session manager. */
private SessionManager<Session> sessionManager;
ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
- Session idpSession = null;
- Cookie idpSessionCookie = getIdPSessionCookie(httpRequest);
- if (idpSessionCookie != null) {
- idpSession = sessionManager.getSession(idpSessionCookie.getValue());
- if (idpSession != null) {
- log.trace("Updating IdP session activity time and adding session object to the request");
- idpSession.setLastActivityInstant(new DateTime());
- MDC.put("idpSessionId", idpSession.getSessionID());
- httpRequest.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, idpSession);
- }
+ Cookie sessionCookie = getIdPSessionCookie(httpRequest);
+ Session idpSession = validateCookie(sessionCookie, httpRequest);
+ if (idpSession != null) {
+ log.trace("Updating IdP session activity time and adding session object to the request");
+ idpSession.setLastActivityInstant(new DateTime());
+ MDC.put("idpSessionId", idpSession.getSessionID());
+ httpRequest.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, idpSession);
}
filterChain.doFilter(request, response);
}
sessionManager = (SessionManager<Session>) filterConfig.getServletContext().getAttribute(sessionManagerId);
+
+ String consistentAddressParam = filterConfig.getInitParameter("ensureConsistentClientAddress");
+ if (DatatypeHelper.isEmpty(consistentAddressParam)) {
+ consistentAddress = true;
+ } else {
+ consistentAddress = Boolean.parseBoolean(consistentAddressParam);
+ }
}
/**
* Gets the IdP session cookie from the current request, if the user currently has a session.
*
- * @param request current HTTP request
+ * @param httpRequest current HTTP request
*
* @return the user's current IdP session cookie, if they have a current session, otherwise null
*/
- protected Cookie getIdPSessionCookie(HttpServletRequest request) {
+ protected Cookie getIdPSessionCookie(HttpServletRequest httpRequest) {
log.trace("Attempting to retrieve IdP session cookie.");
- Cookie[] requestCookies = request.getCookies();
+ Cookie[] requestCookies = httpRequest.getCookies();
if (requestCookies != null) {
for (Cookie requestCookie : requestCookies) {
}
}
- log.trace("No IdP session cookie sent by the client.");
return null;
}
+
+ /**
+ * Validates the given session cookie against the associated session.
+ *
+ * @param sessionCookie the session cookie
+ * @param httpRequest the current HTTP request
+ *
+ * @return the session against which the cookie was validated
+ */
+ protected Session validateCookie(Cookie sessionCookie, HttpServletRequest httpRequest) {
+ if (sessionCookie == null) {
+ return null;
+ }
+
+ // index 0: remote address
+ // index 1: session ID
+ // index 2: Base64(HMAC(index 0 + index 1))
+ String[] valueComponents = sessionCookie.getValue().split("\\|");
+
+ if (consistentAddress) {
+ if (!httpRequest.getRemoteAddr().equals(valueComponents[0])) {
+ log.error("Client sent a cookie from addres {} but the cookie was issued to address {}", httpRequest
+ .getRemoteAddr(), valueComponents[0]);
+ return null;
+ }
+ }
+
+ Session userSession = sessionManager.getSession(valueComponents[1]);
+
+ if (userSession != null) {
+ SecretKey signingKey = userSession.getSessionSecretKey();
+ try {
+ Mac mac = Mac.getInstance("HmacSHA256");
+ mac.init(signingKey);
+ mac.update(valueComponents[0].getBytes());
+ mac.update(valueComponents[1].getBytes());
+ byte[] signature = mac.doFinal();
+
+ if (!DatatypeHelper.safeEquals(valueComponents[2], Base64.encodeBytes(signature))) {
+ log.error("Session cookie signature did not match, the session cookie has been tampered with");
+ return null;
+ }
+ } catch (GeneralSecurityException e) {
+ log.error("Unable to computer over session cookie material", e);
+ }
+ } else {
+ log.debug("No session associated with session ID {} - session must have timed out",
+ valueComponents[1]);
+ }
+ return userSession;
+ }
}
\ No newline at end of file
import java.util.Map;
+import javax.crypto.SecretKey;
+
/**
* Session information for user logged into the IdP.
*/
/** Name of the HTTP request attribute to which a users IdP session is bound. */
public static final String HTTP_SESSION_BINDING_ATTRIBUTE = "ShibbolethIdPSession";
+
+ /**
+ * A secret key associated with this session.
+ *
+ * @return secret key associated with this session
+ */
+ public SecretKey getSessionSecretKey();
/**
* Gets the methods by which the user has authenticated to the IdP.
import java.util.HashMap;
import java.util.Map;
+import javax.crypto.SecretKey;
+
import edu.internet2.middleware.shibboleth.common.session.impl.AbstractSession;
import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
/** Serial version UID. */
private static final long serialVersionUID = 2927868242208211623L;
+
+ /** Secret key associated with the session. */
+ private SecretKey sessionKey;
/** The list of methods used to authenticate the user. */
private HashMap<String, AuthenticationMethodInformation> authnMethods;
* Constructor.
*
* @param sessionId ID of the session
+ * @param key a secret key to associate with the session
* @param timeout inactivity timeout for the session in milliseconds
*/
- public SessionImpl(String sessionId, long timeout) {
+ public SessionImpl(String sessionId, SecretKey key, long timeout) {
super(sessionId, timeout);
+ sessionKey = key;
+
authnMethods = new HashMap<String, AuthenticationMethodInformation>();
servicesInformation = new HashMap<String, ServiceInformation>();
}
+
+ /** {@inheritDoc} */
+ public SecretKey getSessionSecretKey() {
+ return sessionKey;
+ }
/** {@inheritDoc} */
public Map<String, AuthenticationMethodInformation> getAuthenticationMethods() {
package edu.internet2.middleware.shibboleth.idp.session.impl;
+import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.List;
import java.util.Vector;
+import javax.crypto.KeyGenerator;
+
import org.apache.commons.ssl.util.Hex;
import org.joda.time.DateTime;
import org.opensaml.util.storage.ExpiringObject;
/** Spring context used to publish login and logout events. */
private ApplicationContext appCtx;
+ /** Generator used to create secret keys associated with the session. */
+ private KeyGenerator secretKeyGen;
+
/** Number of random bits within a session ID. */
private final int sessionIDSize = 32;
sessionStore = storageService;
partition = "session";
sessionLifetime = lifetime;
+
+ try {
+ secretKeyGen = KeyGenerator.getInstance("AES");
+ } catch (NoSuchAlgorithmException e) {
+ log.error("AES key generation is not supported", e);
+ }
}
/**
prng.nextBytes(sid);
String sessionID = Hex.encode(sid);
- Session session = new SessionImpl(sessionID, sessionLifetime);
+ Session session = new SessionImpl(sessionID, secretKeyGen.generateKey(), sessionLifetime);
SessionManagerEntry sessionEntry = new SessionManagerEntry(session, sessionLifetime);
sessionStore.put(partition, sessionID, sessionEntry);
MDC.put("idpSessionId", sessionID);
- Session session = new SessionImpl(sessionID, sessionLifetime);
+ Session session = new SessionImpl(sessionID, secretKeyGen.generateKey(), sessionLifetime);
SessionManagerEntry sessionEntry = new SessionManagerEntry(session, sessionLifetime);
sessionStore.put(partition, sessionID, sessionEntry);
log.trace("Created session {}", sessionID);
/** {@inheritDoc} */
public void onApplicationEvent(ApplicationEvent event) {
- if(event instanceof AddEntryEvent){
- AddEntryEvent addEvent = (AddEntryEvent)event;
- if(addEvent.getValue() instanceof SessionManagerEntry){
+ if (event instanceof AddEntryEvent) {
+ AddEntryEvent addEvent = (AddEntryEvent) event;
+ if (addEvent.getValue() instanceof SessionManagerEntry) {
SessionManagerEntry sessionEntry = (SessionManagerEntry) addEvent.getValue();
appCtx.publishEvent(new LoginEvent(sessionEntry.getSession()));
}
}
-
+
if (event instanceof RemoveEntryEvent) {
RemoveEntryEvent removeEvent = (RemoveEntryEvent) event;
if (removeEvent.getValue() instanceof SessionManagerEntry) {
/** {@inheritDoc} */
public void setApplicationContext(ApplicationContext applicationContext) {
ApplicationContext rootContext = applicationContext;
- while(rootContext.getParent() != null){
+ while (rootContext.getParent() != null) {
rootContext = rootContext.getParent();
}
appCtx = rootContext;