mirror of
https://github.com/apache/struts.git
synced 2026-08-11 01:27:14 +00:00
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:
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user