/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2013 The ZAP Development Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zaproxy.zap.session;
import java.net.HttpCookie;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import org.parosproxy.paros.network.HttpMessage;
import org.zaproxy.zap.extension.httpsessions.HttpSession;
import org.zaproxy.zap.extension.httpsessions.HttpSessionTokensSet;
/**
* Helper for Cookie-based session management.
*/
public class CookieBasedSessionManagementHelper {
private static final Logger log = Logger.getLogger(CookieBasedSessionManagementHelper.class);
/**
* Modifies a message so its Request Header/Body matches the web session provided.
*
* @param message the message
* @param session the session
*/
public static void processMessageToMatchSession(HttpMessage message, HttpSession session) {
processMessageToMatchSession(message, message.getRequestHeader().getHttpCookies(), session);
}
/**
* Modifies a message so its Request Header/Body matches the web session provided.
*
* @param message the message
* @param requestCookies a pre-computed list with the request cookies (for optimization reasons)
* @param session the session
*/
public static void processMessageToMatchSession(HttpMessage message, List<HttpCookie> requestCookies,
HttpSession session) {
// Make a copy of the session tokens set, as they will be modified
HttpSessionTokensSet tokensSet = session.getTokensNames();
// If no tokens exists create dummy Object -> NPE
if (tokensSet == null) {
tokensSet = new HttpSessionTokensSet();
}
Set<String> unsetSiteTokens = new LinkedHashSet<>(tokensSet.getTokensSet());
// Iterate through the cookies in the request
Iterator<HttpCookie> it = requestCookies.iterator();
while (it.hasNext()) {
HttpCookie cookie = it.next();
String cookieName = cookie.getName();
// If the cookie is a token
if (tokensSet.isSessionToken(cookieName)) {
String tokenValue = session.getTokenValue(cookieName);
if (log.isDebugEnabled())
log.debug("Changing value of token '" + cookieName + "' to: " + tokenValue);
// Change it's value to the one in the active session, if any
if (tokenValue != null) {
cookie.setValue(tokenValue);
}// Or delete it, if the active session does not have a token value
else {
it.remove();
}
// Remove the token from the token set so we know what tokens still have to be
// added
unsetSiteTokens.remove(cookieName);
}
}
// Iterate through the tokens that are not present in the request and set the proper
// value
for (String token : unsetSiteTokens) {
String tokenValue = session.getTokenValue(token);
// Change it's value to the one in the active session, if any
if (tokenValue != null) {
if (log.isDebugEnabled())
log.debug("Adding token '" + token + " with value: " + tokenValue);
HttpCookie cookie = new HttpCookie(token, tokenValue);
requestCookies.add(cookie);
}
}
// Store the session in the HttpMessage for caching purpose
message.setHttpSession(session);
// Update the cookies in the message
message.getRequestHeader().setCookies(requestCookies);
}
/**
* Gets the matching http session, if any, for a particular message containing a list of
* cookies, from a set of sessions.
*
* @param sessions the existing sessions
* @param cookies the cookies present in the request header of the message
* @param siteTokens the tokens
* @return the matching http session, if any, or null if no existing session was found to match
* all the tokens
*/
public static HttpSession getMatchingHttpSession(final Collection<HttpSession> sessions,
List<HttpCookie> cookies, final HttpSessionTokensSet siteTokens) {
// Pre-checks
if (sessions.isEmpty()) {
return null;
}
List<HttpSession> matchingSessions = new LinkedList<>(sessions);
for (String token : siteTokens.getTokensSet()) {
// Get the corresponding cookie from the cookies list
HttpCookie matchingCookie = null;
for (HttpCookie cookie : cookies) {
if (cookie.getName().equals(token)) {
matchingCookie = cookie;
break;
}
}
// Filter the sessions that do not match the cookie value
Iterator<HttpSession> it = matchingSessions.iterator();
while (it.hasNext()) {
if (!it.next().matchesToken(token, matchingCookie)) {
it.remove();
}
}
}
// Return the matching session
if (matchingSessions.size() >= 1) {
if (matchingSessions.size() > 1) {
log.warn("Multiple sessions matching the cookies from response. Using first one.");
}
return matchingSessions.get(0);
}
return null;
}
}