WW-4227 Adds first step to define internal security mechanism

git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@1533336 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Lukasz Lenart
2013-10-18 06:47:31 +00:00
parent 18a9ea1742
commit b7cea78bfe
14 changed files with 241 additions and 0 deletions
@@ -268,4 +268,7 @@ public final class StrutsConstants {
/** actions names' whitelist **/
public static final String STRUTS_ALLOWED_ACTION_NAMES = "struts.allowed.action.names";
/** Security firewall **/
public static final String STRUTS_SECURITY_GATE = "struts.securityGate";
}
@@ -70,6 +70,7 @@ import org.apache.struts2.components.UrlRenderer;
import org.apache.struts2.dispatcher.StaticContentLoader;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
import org.apache.struts2.security.SecurityGate;
import org.apache.struts2.views.freemarker.FreemarkerManager;
import org.apache.struts2.views.util.UrlHelper;
import org.apache.struts2.views.velocity.VelocityManager;
@@ -406,6 +407,8 @@ public class BeanSelectionProvider implements ConfigurationProvider {
alias(TextParser.class, StrutsConstants.STRUTS_EXPRESSION_PARSER, builder, props);
alias(SecurityGate.class, StrutsConstants.STRUTS_SECURITY_GATE, builder, props);
if ("true".equalsIgnoreCase(props.getProperty(StrutsConstants.STRUTS_DEVMODE))) {
props.setProperty(StrutsConstants.STRUTS_I18N_RELOAD, "true");
props.setProperty(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, "true");
@@ -65,6 +65,7 @@ import org.apache.struts2.config.StrutsXmlConfigurationProvider;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
import org.apache.struts2.security.SecurityGate;
import org.apache.struts2.util.AttributeMap;
import org.apache.struts2.util.ObjectFactoryDestroyable;
import org.apache.struts2.util.fs.JBossFileManager;
@@ -209,6 +210,7 @@ public class Dispatcher {
private ValueStackFactory valueStackFactory;
private SecurityGate securityGate;
/**
* Create the Dispatcher instance for a given ServletContext and set of initialization parameters.
@@ -281,6 +283,11 @@ public class Dispatcher {
this.handleException = Boolean.parseBoolean(handleException);
}
@Inject
public void setSecurityGate(SecurityGate securityGate) {
this.securityGate = securityGate;
}
/**
* Releases all instances bound to this dispatcher instance.
*/
@@ -929,6 +936,15 @@ public class Dispatcher {
ContainerHolder.clear();
}
/**
* Checks if request doesn't contain suspicious values
*
* @param request current {@link HttpServletRequest}
*/
public void checkRequest(HttpServletRequest request) {
securityGate.check(request);
}
/**
* Provide an accessor class for static XWork utility.
*/
@@ -158,6 +158,7 @@ public class PrepareOperations {
ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
if (mapping == null || forceLookup) {
try {
dispatcher.checkRequest(request);
mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
if (mapping != null) {
request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
@@ -0,0 +1,56 @@
package org.apache.struts2.security;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import org.apache.struts2.StrutsConstants;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Default implementation of {@link org.apache.struts2.security.SecurityGate}
* just examines all the defined {@link org.apache.struts2.security.SecurityGuard}'s
*/
public class DefaultSecurityGate implements SecurityGate {
private static final Logger LOG = LoggerFactory.getLogger(DefaultSecurityGate.class);
private List<SecurityGuard> guards;
private boolean devMode;
@Inject(StrutsConstants.STRUTS_DEVMODE)
public void setDevMode(String devMode) {
this.devMode = "true".equalsIgnoreCase(devMode);
}
@Inject
public void setContainer(Container container) {
guards = new ArrayList<SecurityGuard>();
Set<String> guardNames = container.getInstanceNames(SecurityGate.class);
for (String guardName : guardNames) {
SecurityGuard guard = container.getInstance(SecurityGuard.class, guardName);
if (guard != null) {
guards.add(guard);
} else if (devMode) {
LOG.debug("Got null instance of [#0] for name [#1]", SecurityGuard.class.getSimpleName(), guardName);
}
}
}
public void check(HttpServletRequest request) {
for (SecurityGuard guard : guards) {
SecurityPass pass = guard.accept(request);
if (pass.isNotAccepted()) {
if (LOG.isDebugEnabled()) {
LOG.debug("[#0] didn't accept the request!", guard.getClass().getName());
}
throw new StrutsSecurityException(pass.getGuardMessage());
}
}
}
}
@@ -0,0 +1,14 @@
package org.apache.struts2.security;
import javax.servlet.http.HttpServletRequest;
/**
* Checks if parameter name is valida and it doesn't contain vulnerable code
*/
public class ParameterNameSecurityGuard implements SecurityGuard {
public SecurityPass accept(HttpServletRequest request) {
return SecurityPass.accepted();
}
}
@@ -0,0 +1,14 @@
package org.apache.struts2.security;
import javax.servlet.http.HttpServletRequest;
/**
* Checks if parameter's value doesn't contain vulnerable code
*/
public class ParameterValueSecurityGuard implements SecurityGuard {
public SecurityPass accept(HttpServletRequest request) {
return SecurityPass.accepted();
}
}
@@ -0,0 +1,12 @@
package org.apache.struts2.security;
import javax.servlet.http.HttpServletRequest;
/**
* Main
*/
public interface SecurityGate {
void check(HttpServletRequest request);
}
@@ -0,0 +1,12 @@
package org.apache.struts2.security;
import javax.servlet.http.HttpServletRequest;
/**
* TODO lukaszlenart: write a JavaDoc
*/
public interface SecurityGuard {
SecurityPass accept(HttpServletRequest request);
}
@@ -0,0 +1,36 @@
package org.apache.struts2.security;
/**
* TODO lukaszlenart: write a JavaDoc
*/
public class SecurityPass {
private Boolean accepted;
private final String message;
public static SecurityPass accepted() {
return new SecurityPass(true, null);
}
public static SecurityPass notAccepted(String message) {
return new SecurityPass(false, message);
}
private SecurityPass(boolean accepted, String message) {
this.accepted = accepted;
this.message = message;
}
public String getGuardMessage() {
return message;
}
public boolean isAccepted() {
return accepted;
}
public boolean isNotAccepted() {
return !accepted;
}
}
@@ -0,0 +1,14 @@
package org.apache.struts2.security;
import org.apache.struts2.StrutsException;
/**
* Exception indicates possible security breach
*/
public class StrutsSecurityException extends StrutsException {
public StrutsSecurityException(String message) {
super(message);
}
}
@@ -134,6 +134,10 @@
<bean type="ognl.PropertyAccessor" name="java.util.HashSet" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" />
<bean type="ognl.PropertyAccessor" name="java.util.HashMap" class="com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor" />
<bean type="org.apache.struts2.security.SecurityGate" name="struts" class="org.apache.struts2.security.DefaultSecurityGate" scope="singleton"/>
<bean type="org.apache.struts2.security.SecurityGuard" name="parameterNameGuard" class="org.apache.struts2.security.ParameterNameSecurityGuard" scope="singleton"/>
<bean type="org.apache.struts2.security.SecurityGuard" name="parameterValueGuard" class="org.apache.struts2.security.ParameterValueSecurityGuard" scope="singleton"/>
<package name="struts-default" abstract="true">
<result-types>
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
@@ -0,0 +1,28 @@
package org.apache.struts2.security;
import com.mockobjects.servlet.MockHttpServletRequest;
import org.junit.Test;
import javax.servlet.http.HttpServletRequest;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class ParameterNameSecurityGuardTest {
@Test
public void shouldPass() throws Exception {
// given
SecurityGuard guard = new ParameterNameSecurityGuard();
HttpServletRequest request = new MockHttpServletRequest();
// when
SecurityPass pass = guard.accept(request);
// then
assertTrue(pass.isAccepted());
assertNull(pass.getGuardMessage());
}
}
@@ -0,0 +1,28 @@
package org.apache.struts2.security;
import com.mockobjects.servlet.MockHttpServletRequest;
import org.junit.Test;
import javax.servlet.http.HttpServletRequest;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class ParameterValueSecurityGuardTest {
@Test
public void shouldPass() throws Exception {
// given
SecurityGuard guard = new ParameterValueSecurityGuard();
HttpServletRequest request = new MockHttpServletRequest();
// when
SecurityPass pass = guard.accept(request);
// then
assertTrue(pass.isAccepted());
assertNull(pass.getGuardMessage());
}
}