package org.zaproxy.zap.session; import java.lang.ref.WeakReference; import java.net.HttpCookie; import java.util.Iterator; import java.util.List; import net.sf.json.JSONObject; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.httpclient.Cookie; import org.apache.commons.httpclient.HttpState; import org.apache.log4j.Logger; import org.parosproxy.paros.Constant; import org.parosproxy.paros.control.Control; import org.parosproxy.paros.db.DatabaseException; import org.parosproxy.paros.extension.ExtensionHook; import org.parosproxy.paros.model.Model; import org.parosproxy.paros.model.Session; import org.parosproxy.paros.network.HttpMessage; import org.zaproxy.zap.extension.api.ApiDynamicActionImplementor; import org.zaproxy.zap.extension.api.ApiException; import org.zaproxy.zap.extension.api.ApiResponse; import org.zaproxy.zap.extension.api.ApiResponseElement; import org.zaproxy.zap.extension.httpsessions.ExtensionHttpSessions; import org.zaproxy.zap.extension.httpsessions.HttpSessionTokensSet; import org.zaproxy.zap.extension.sessions.SessionManagementAPI; import org.zaproxy.zap.model.Context; import org.zaproxy.zap.utils.ApiUtils; /** * The type corresponding to a {@link SessionManagementMethod} for web applications that use cookies * for session management. */ public class CookieBasedSessionManagementMethodType extends SessionManagementMethodType { private static final int METHOD_IDENTIFIER = 0; private static final Logger log = Logger.getLogger(CookieBasedSessionManagementMethod.class); /** The Constant METHOD_NAME. */ private static final String METHOD_NAME = Constant.messages.getString("sessionmanagement.method.cb.name"); /** * The implementation for a {@link SessionManagementMethod} that for web applications that use * cookies for session management. */ public static class CookieBasedSessionManagementMethod implements SessionManagementMethod { private int contextId; private Context context; private static WeakReference<ExtensionHttpSessions> extHttpSessions; public CookieBasedSessionManagementMethod(int contextId) { this.contextId = contextId; } @Override public String toString() { return CookieBasedSessionManagementMethodType.METHOD_NAME; } @Override public boolean isConfigured() { // Always configured return true; } private Cookie convertCookie(HttpCookie cookie) { Cookie c = new Cookie(cookie.getDomain(), cookie.getName(), cookie.getValue(), cookie.getPath(), (int) cookie.getMaxAge(), cookie.getSecure()); c.setVersion(cookie.getVersion()); c.setComment(cookie.getComment()); return c; } @Override public WebSession extractWebSession(HttpMessage msg) { if (msg.getRequestingUser() != null) return msg.getRequestingUser().getAuthenticatedSession(); else { // Make sure any cookies in the message are put in the session CookieBasedSession session = new CookieBasedSession(); for (HttpCookie c : msg.getRequestHeader().getHttpCookies()) session.getHttpState().addCookie(convertCookie(c)); // Use the messages hostname as default domain when generating SET cookies for (HttpCookie c : msg.getResponseHeader().getHttpCookies(msg.getRequestHeader().getHostName())) session.getHttpState().addCookie(convertCookie(c)); return session; } } @Override public void processMessageToMatchSession(HttpMessage message, WebSession session) throws UnsupportedWebSessionException { if (session.getHttpState() == null) return; session.getHttpState().purgeExpiredCookies(); // Remove any cookies that will be added by the HttpState from the message List<HttpCookie> cookies = message.getRequestHeader().getHttpCookies(); Iterator<HttpCookie> it = cookies.iterator(); while (it.hasNext()) { HttpCookie c = it.next(); for (Cookie sc : session.getHttpState().getCookies()) if (sc.getName().equals(c.getName())) { it.remove(); break; } } message.setCookies(cookies); } private ExtensionHttpSessions getHttpSessionsExtension() { if (extHttpSessions == null || extHttpSessions.get() == null) { extHttpSessions = new WeakReference<>( Control .getSingleton().getExtensionLoader().getExtension(ExtensionHttpSessions.class)); if (extHttpSessions == null) log.error("An error occured while loading the ExtensionHttpSessions."); } return extHttpSessions.get(); } private Context getContext() { if (context == null) { context = Model.getSingleton().getSession().getContext(contextId); } return context; } @Override public SessionManagementMethod clone() { return new CookieBasedSessionManagementMethod(contextId); } @Override public void clearWebSessionIdentifiers(HttpMessage msg) { HttpSessionTokensSet tokens = getHttpSessionsExtension().getHttpSessionTokensSetForContext( getContext()); if (tokens == null) { log.info("No tokens to clear."); return; } List<HttpCookie> requestCookies = msg.getRequestHeader().getHttpCookies(); Iterator<HttpCookie> it = requestCookies.iterator(); while (it.hasNext()) if (tokens.isSessionToken(it.next().getName())) it.remove(); msg.setCookies(requestCookies); } @Override public SessionManagementMethodType getType() { return new CookieBasedSessionManagementMethodType(); } @Override public ApiResponse getApiResponseRepresentation() { return new ApiResponseElement("methodName", API_METHOD_NAME); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + contextId; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; CookieBasedSessionManagementMethod other = (CookieBasedSessionManagementMethod) obj; if (contextId != other.contextId) return false; return true; } @Override public WebSession createEmptyWebSession() { return new CookieBasedSession(); } } /** * The implementation for a {@link WebSession} used in cases where sessions are managed using * cookies. */ public static class CookieBasedSession extends WebSession { private static int generatedNameIndex; public CookieBasedSession(String name) { super(name, new HttpState()); } public CookieBasedSession() { super("Cookie Based Session " + generatedNameIndex++, new HttpState()); } } @Override public CookieBasedSessionManagementMethod createSessionManagementMethod(int contextId) { return new CookieBasedSessionManagementMethod(contextId); } @Override public String getName() { return METHOD_NAME; } @Override public AbstractSessionManagementMethodOptionsPanel buildOptionsPanel(Context uiSharedContext) { // No need for a configuration panel yet return null; } @Override public boolean hasOptionsPanel() { return false; } @Override public boolean isTypeForMethod(SessionManagementMethod method) { return (method instanceof CookieBasedSessionManagementMethod); } @Override public void hook(ExtensionHook extensionHook) { // No need for hooking anything } @Override public int getUniqueIdentifier() { return METHOD_IDENTIFIER; } @Override public SessionManagementMethod loadMethodFromSession(Session session, int contextId) throws DatabaseException { return new CookieBasedSessionManagementMethod(contextId); } @Override public void persistMethodToSession(Session session, int contextId, SessionManagementMethod method) throws UnsupportedSessionManagementMethodException, DatabaseException { // Nothing to persist } @Override public void exportData(Configuration config, SessionManagementMethod sessionMethod) { // nothing to do } @Override public void importData(Configuration config, SessionManagementMethod sessionMethod) throws ConfigurationException { // nothing to do } private static final String API_METHOD_NAME = "cookieBasedSessionManagement"; @Override public ApiDynamicActionImplementor getSetMethodForContextApiAction() { return new ApiDynamicActionImplementor(API_METHOD_NAME, null, null) { @Override public void handleAction(JSONObject params) throws ApiException { Context context = ApiUtils.getContextByParamId(params, SessionManagementAPI.PARAM_CONTEXT_ID); context.setSessionManagementMethod(createSessionManagementMethod(context.getIndex())); } }; } }