All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.struts2.interceptor.CookieInterceptor Maven / Gradle / Ivy

There is a newer version: 6.4.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.struts2.interceptor;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ServletActionContext;

import javax.servlet.http.Cookie;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * 
 *
 * 

* The aim of this interceptor is to set values in the stack/action based on cookie name/value * of interest. *

* *

* If an asterisk is present in cookiesName parameter, it will be assume that * all cookies name are to be injected into struts' action, even though * cookiesName is comma-separated by other values, e.g. (cookie1,*,cookie2). *

* *

* If cookiesName is left empty it will assume that no cookie will be injected * into Struts' action. *

* *

* If an asterisk is present in cookiesValue parameter, it will assume that all * cookies name irrespective of its value will be injected into Struts' action so * long as the cookie name matches those specified in cookiesName parameter. *

* *

* If cookiesValue is left empty it will assume that all cookie that match the cookieName * parameter will be injected into Struts' action. *

* *

* The action could implement {@link CookiesAware} in order to have a {@link Map} * of filtered cookies set into it. *

* * * * * * *
    *
  • cookiesName (mandatory) - Name of cookies to be injected into the action. If more * than one cookie name is desired it could be comma-separated. * If all cookies name is desired, it could simply be *, an asterik. * When many cookies name are comma-separated either of the cookie * that match the name in the comma-separated list will be qualified.
  • *
  • cookiesValue (mandatory) - Value of cookies that if its name matches cookieName attribute * and its value matched this, will be injected into Struts' * action. If more than one cookie name is desired it could be * comma-separated. If left empty, it will assume any value would * be ok. If more than one value is specified (comma-separated) * it will assume a match if either value is matched.
  • *
  • acceptCookieNames (optional) - Pattern used to check if name of cookie matches the provided patter, to
  • *
* * * * * * *
    *
  • * populateCookieValueIntoStack - this method will decide if this cookie value is qualified * to be populated into the value stack (hence into the action itself) *
  • *
  • * injectIntoCookiesAwareAction - this method will inject selected cookies (as a java.util.Map) * into action that implements {@link CookiesAware}. *
  • *
* * * *
 * 
 *
 * <!--
 *   This example will inject cookies named either 'cookie1' or 'cookie2' whose
 *   value could be either 'cookie1value' or 'cookie2value' into Struts' action.
 * -->
 * <action ... >
 *    <interceptor-ref name="cookie">
 *        <param name="cookiesName">cookie1, cookie2</param>
 *        <param name="cookiesValue">cookie1value, cookie2value</param>
 *    </interceptor-ref>
 *    ....
 * </action>
 *
 *
 * <!--
 *      This example will inject cookies named either 'cookie1' or 'cookie2'
 *     regardless of their value into Struts' action.
 * -->
 * <action ... >
 *   <interceptor-ref name="cookie">
 *      <param name="cookiesName">cookie1, cookie2</param>
 *      <param name="cookiesValue">*</param>
 *   <interceptor-ref>
 *   ...
 * </action>
 *
 *
 * <!--
 *      This example will inject cookies named either 'cookie1' with value
 *      'cookie1value' or 'cookie2' with value 'cookie2value' into Struts'
 *      action.
 * -->
 * <action ... >
 *   <interceptor-ref name="cookie">
 *      <param name="cookiesName">cookie1</param>
 *      <param name="cookiesValue">cookie1value</param>
 *   </interceptor-ref>
 *   <interceptor-ref name="cookie">
 *      <param name="cookiesName"<cookie2</param>
 *     <param name="cookiesValue">cookie2value</param>
 *   </interceptor-ref>
 *   ....
 * </action>
 *
 * <!--
 *    This example will inject any cookies regardless of its value into
 *    Struts' action.
 *  -->
 * <action ... >
 *   <interceptor-ref name="cookie">
 *      <param name="cookiesName">*</param>
 *      <param name="cookiesValue">*</param>
 *   </interceptor-ref>
 *    ...
 * </action>
 *
 * 
 * 
* * @see CookiesAware */ public class CookieInterceptor extends AbstractInterceptor { private static final long serialVersionUID = 4153142432948747305L; private static final Logger LOG = LogManager.getLogger(CookieInterceptor.class); private static final String ACCEPTED_PATTERN = "[a-zA-Z0-9\\.\\]\\[_'\\s]+"; private Set cookiesNameSet = Collections.emptySet(); private Set cookiesValueSet = Collections.emptySet(); private ExcludedPatternsChecker excludedPatternsChecker; private AcceptedPatternsChecker acceptedPatternsChecker; @Inject public void setExcludedPatternsChecker(ExcludedPatternsChecker excludedPatternsChecker) { this.excludedPatternsChecker = excludedPatternsChecker; } @Inject public void setAcceptedPatternsChecker(AcceptedPatternsChecker acceptedPatternsChecker) { this.acceptedPatternsChecker = acceptedPatternsChecker; this.acceptedPatternsChecker.setAcceptedPatterns(ACCEPTED_PATTERN); } /** * @param cookiesName the cookiesName which if matched will allow the cookie * to be injected into action, could be comma-separated string. */ public void setCookiesName(String cookiesName) { if (cookiesName != null) { this.cookiesNameSet = TextParseUtil.commaDelimitedStringToSet(cookiesName); } } /** * @param cookiesValue the cookiesValue which if matched (together with matching * cookiesName) will caused the cookie to be injected into action, could be * comma-separated string. */ public void setCookiesValue(String cookiesValue) { if (cookiesValue != null) { this.cookiesValueSet = TextParseUtil.commaDelimitedStringToSet(cookiesValue); } } /** * Set the acceptCookieNames pattern of allowed names of cookies * to protect against remote command execution vulnerability. * * @param commaDelimitedPattern is used to check cookie name against, can set of comma delimited patterns */ public void setAcceptCookieNames(String commaDelimitedPattern) { acceptedPatternsChecker.setAcceptedPatterns(commaDelimitedPattern); } public String intercept(ActionInvocation invocation) throws Exception { LOG.debug("start interception"); // contains selected cookies final Map cookiesMap = new LinkedHashMap<>(); Cookie[] cookies = ServletActionContext.getRequest().getCookies(); if (cookies != null) { final ValueStack stack = ActionContext.getContext().getValueStack(); for (Cookie cookie : cookies) { String name = cookie.getName(); String value = cookie.getValue(); if (isAcceptableName(name)) { if (cookiesNameSet.contains("*")) { LOG.debug("Contains cookie name [*] in configured cookies name set, cookie with name [{}] with value [{}] will be injected", name, value); populateCookieValueIntoStack(name, value, cookiesMap, stack); } else if (cookiesNameSet.contains(cookie.getName())) { populateCookieValueIntoStack(name, value, cookiesMap, stack); } } else { LOG.warn("Cookie name [{}] with value [{}] was rejected!", name, value); } } } // inject the cookiesMap, even if we don't have any cookies injectIntoCookiesAwareAction(invocation.getAction(), cookiesMap); return invocation.invoke(); } /** * Checks if name of Cookie doesn't contain vulnerable code * * @param name of Cookie * @return true|false */ protected boolean isAcceptableName(String name) { return !isExcluded(name) && isAccepted(name); } /** * Checks if name/value of Cookie is acceptable * * @param name of Cookie * @return true|false */ protected boolean isAccepted(String name) { AcceptedPatternsChecker.IsAccepted accepted = acceptedPatternsChecker.isAccepted(name); if (accepted.isAccepted()) { if (LOG.isTraceEnabled()) { LOG.trace("Cookie [{}] matches acceptedPattern [{}]", name, accepted.getAcceptedPattern()); } return true; } if (LOG.isTraceEnabled()) { LOG.trace("Cookie [{}] doesn't match acceptedPattern [{}]", name, accepted.getAcceptedPattern()); } return false; } /** * Checks if name/value of Cookie is excluded * * @param name of Cookie * @return true|false */ protected boolean isExcluded(String name) { ExcludedPatternsChecker.IsExcluded excluded = excludedPatternsChecker.isExcluded(name); if (excluded.isExcluded()) { if (LOG.isTraceEnabled()) { LOG.trace("Cookie [{}] matches excludedPattern [{}]", name, excluded.getExcludedPattern()); } return true; } if (LOG.isTraceEnabled()) { LOG.trace("Cookie [{}] doesn't match excludedPattern [{}]", name, excluded.getExcludedPattern()); } return false; } /** * Hook that populate cookie value into value stack (hence the action) * if the criteria is satisfied (if the cookie value matches with those configured). * * @param cookieName cookie name * @param cookieValue cookie value * @param cookiesMap map of cookies * @param stack value stack */ protected void populateCookieValueIntoStack(String cookieName, String cookieValue, Map cookiesMap, ValueStack stack) { if (cookiesValueSet.isEmpty() || cookiesValueSet.contains("*")) { // If the interceptor is configured to accept any cookie value // OR // no cookiesValue is defined, so as long as the cookie name match // we'll inject it into Struts' action if (LOG.isDebugEnabled()) { if (cookiesValueSet.isEmpty()) LOG.debug("no cookie value is configured, cookie with name [{}] with value [{}] will be injected", cookieName, cookieValue); else if (cookiesValueSet.contains("*")) LOG.debug("interceptor is configured to accept any value, cookie with name [{}] with value [{}] will be injected", cookieName, cookieValue); } cookiesMap.put(cookieName, cookieValue); stack.setValue(cookieName, cookieValue); } else { // if cookiesValues is specified, the cookie's value must match before we // inject them into Struts' action if (cookiesValueSet.contains(cookieValue)) { LOG.debug("both configured cookie name and value matched, cookie [{}] with value [{}] will be injected", cookieName, cookieValue); cookiesMap.put(cookieName, cookieValue); stack.setValue(cookieName, cookieValue); } } } /** * Hook that set the cookiesMap into action that implements * {@link CookiesAware}. * * @param action action object * @param cookiesMap map of cookies */ protected void injectIntoCookiesAwareAction(Object action, Map cookiesMap) { if (action instanceof CookiesAware) { LOG.debug("Action [{}] implements CookiesAware, injecting cookies map [{}]", action, cookiesMap); ((CookiesAware)action).setCookiesMap(cookiesMap); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy