2 * Copyright 2008 University Corporation for Advanced Internet Development, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package edu.internet2.middleware.shibboleth.idp.session;
19 import java.io.IOException;
20 import java.security.GeneralSecurityException;
21 import java.util.Arrays;
23 import javax.crypto.Mac;
24 import javax.crypto.SecretKey;
25 import javax.servlet.Filter;
26 import javax.servlet.FilterChain;
27 import javax.servlet.FilterConfig;
28 import javax.servlet.ServletException;
29 import javax.servlet.ServletRequest;
30 import javax.servlet.ServletResponse;
31 import javax.servlet.http.Cookie;
32 import javax.servlet.http.HttpServletRequest;
34 import org.joda.time.DateTime;
35 import org.opensaml.xml.util.Base64;
36 import org.opensaml.xml.util.DatatypeHelper;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
41 import edu.internet2.middleware.shibboleth.common.session.SessionManager;
42 import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationEngine;
45 * A filter that adds the current users {@link Session} the request, if the user has a session.
47 public class IdPSessionFilter implements Filter {
50 private final Logger log = LoggerFactory.getLogger(IdPSessionFilter.class);
52 /** Whether the client must always come back from the same address. */
53 private boolean consistentAddress;
55 /** IdP session manager. */
56 private SessionManager<Session> sessionManager;
59 public void destroy() {
64 public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException,
66 HttpServletRequest httpRequest = (HttpServletRequest) request;
68 Cookie sessionCookie = getIdPSessionCookie(httpRequest);
69 Session idpSession = validateCookie(sessionCookie, httpRequest);
70 if (idpSession != null) {
71 log.trace("Updating IdP session activity time and adding session object to the request");
72 idpSession.setLastActivityInstant(new DateTime());
73 MDC.put("idpSessionId", idpSession.getSessionID());
74 httpRequest.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, idpSession);
77 filterChain.doFilter(request, response);
81 public void init(FilterConfig filterConfig) throws ServletException {
82 String sessionManagerId = filterConfig.getInitParameter("sessionManagedId");
83 if (DatatypeHelper.isEmpty(sessionManagerId)) {
84 sessionManagerId = "shibboleth.SessionManager";
87 sessionManager = (SessionManager<Session>) filterConfig.getServletContext().getAttribute(sessionManagerId);
89 String consistentAddressParam = filterConfig.getInitParameter("ensureConsistentClientAddress");
90 if (DatatypeHelper.isEmpty(consistentAddressParam)) {
91 consistentAddress = true;
93 consistentAddress = Boolean.parseBoolean(consistentAddressParam);
98 * Gets the IdP session cookie from the current request, if the user currently has a session.
100 * @param httpRequest current HTTP request
102 * @return the user's current IdP session cookie, if they have a current session, otherwise null
104 protected Cookie getIdPSessionCookie(HttpServletRequest httpRequest) {
105 log.trace("Attempting to retrieve IdP session cookie.");
106 Cookie[] requestCookies = httpRequest.getCookies();
108 if (requestCookies != null) {
109 for (Cookie requestCookie : requestCookies) {
110 if (DatatypeHelper.safeEquals(requestCookie.getName(), AuthenticationEngine.IDP_SESSION_COOKIE_NAME)) {
111 log.trace("Found IdP session cookie.");
112 return requestCookie;
121 * Validates the given session cookie against the associated session.
123 * @param sessionCookie the session cookie
124 * @param httpRequest the current HTTP request
126 * @return the session against which the cookie was validated
128 protected Session validateCookie(Cookie sessionCookie, HttpServletRequest httpRequest) {
129 if (sessionCookie == null) {
133 // index 0: remote address
134 // index 1: session ID
135 // index 2: Base64(HMAC(index 0 + index 1))
136 String[] valueComponents = sessionCookie.getValue().split("\\|");
137 byte[] remoteAddressBytes = Base64.decode(valueComponents[0]);
138 byte[] sessionIdBytes = Base64.decode(valueComponents[1]);
139 byte[] signatureBytes = Base64.decode(valueComponents[2]);
141 if (consistentAddress) {
142 String remoteAddress = new String(remoteAddressBytes);
143 if (!httpRequest.getRemoteAddr().equals(remoteAddress)) {
144 log.error("Client sent a cookie from addres {} but the cookie was issued to address {}", httpRequest
145 .getRemoteAddr(), remoteAddress);
150 String sessionId = new String(sessionIdBytes);
151 Session userSession = sessionManager.getSession(sessionId);
153 if (userSession != null) {
154 SecretKey signingKey = userSession.getSessionSecretKey();
156 Mac mac = Mac.getInstance("HmacSHA256");
157 mac.init(signingKey);
158 mac.update(remoteAddressBytes);
159 mac.update(sessionIdBytes);
160 byte[] signature = mac.doFinal();
162 if (!Arrays.equals(signature, signatureBytes)) {
163 log.error("Session cookie signature did not match, the session cookie has been tampered with");
166 } catch (GeneralSecurityException e) {
167 log.error("Unable to computer over session cookie material", e);
170 log.debug("No session associated with session ID {} - session must have timed out",