提交 f7cf2835 编写于 作者: M Matt Sicker 提交者: Daniel Beck

[SECURITY-1774]

上级 f4796521
package jenkins.security;
import jenkins.util.SystemProperties;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.logging.Logger;
@Restricted(NoExternalUse.class)
public class SuspiciousRequestFilter implements Filter {
/** System property name set to true or false to indicate whether or not semicolons should be allowed in URL paths. */
public static final String ALLOW_SEMICOLONS_IN_PATH = SuspiciousRequestFilter.class.getName() + ".allowSemicolonsInPath";
public static boolean allowSemicolonsInPath = SystemProperties.getBoolean(ALLOW_SEMICOLONS_IN_PATH, false);
private static final Logger LOGGER = Logger.getLogger(SuspiciousRequestFilter.class.getName());
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (!allowSemicolonsInPath && httpRequest.getRequestURI().contains(";")) {
LOGGER.warning(() -> "Denying HTTP " + httpRequest.getMethod() + " to " + httpRequest.getRequestURI() +
" as it has an illegal semicolon in the path. This behavior can be overridden by setting the system property " +
ALLOW_SEMICOLONS_IN_PATH + " to true. For more information, see https://jenkins.io/redirect/semicolons-in-urls");
httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "Semicolons are not allowed in the request URI");
} else {
chain.doFilter(request, response);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
......@@ -36,7 +36,10 @@ import hudson.model.UnprotectedRootAction;
import jenkins.model.Jenkins;
import static org.hamcrest.Matchers.containsString;
import jenkins.security.SuspiciousRequestFilter;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Rule;
......@@ -57,6 +60,16 @@ public class CrumbExclusionTest {
@Rule
public JenkinsRule r = new JenkinsRule();
@BeforeClass
public static void prepare() {
SuspiciousRequestFilter.allowSemicolonsInPath = true;
}
@AfterClass
public static void cleanup() {
SuspiciousRequestFilter.allowSemicolonsInPath = false;
}
@Issue("SECURITY-1774")
@Test
public void pathInfo() throws Exception {
......
package jenkins.security;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.WebResponse;
import hudson.ExtensionList;
import hudson.model.UnprotectedRootAction;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.verb.GET;
import javax.annotation.CheckForNull;
import javax.servlet.http.HttpServletResponse;
import java.net.URL;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
@Issue("SECURITY-1774")
public class SuspiciousRequestFilterTest {
@Rule
public JenkinsRule j = new JenkinsRule();
private WebResponse get(String path) throws Exception {
return j.createWebClient()
.withThrowExceptionOnFailingStatusCode(false)
.getPage(new WebRequest(new URL(j.getURL(), path)))
.getWebResponse();
}
@Test
public void denySemicolonInRequestPathByDefault() throws Exception {
WebResponse response = get("foo/bar/..;/?baz=bruh");
assertThat(Foo.getInstance().baz, is(nullValue()));
assertThat(response.getStatusCode(), is(HttpServletResponse.SC_BAD_REQUEST));
assertThat(response.getContentAsString(), containsString("Semicolons are not allowed in the request URI"));
}
@Test
public void allowSemicolonsInRequestPathWhenEscapeHatchEnabled() throws Exception {
SuspiciousRequestFilter.allowSemicolonsInPath = true;
try {
WebResponse response = get("foo/bar/..;/..;/cli?baz=bruh");
assertThat(Foo.getInstance().baz, is("bruh"));
assertThat(response.getStatusCode(), is(HttpServletResponse.SC_OK));
} finally {
SuspiciousRequestFilter.allowSemicolonsInPath = false;
}
}
@Test
public void allowSemicolonsInQueryParameters() throws Exception {
WebResponse response = get("foo/bar?baz=foo;bar=baz");
assertThat(Foo.getInstance().baz, is("foo;bar=baz"));
assertThat(response.getStatusCode(), is(HttpServletResponse.SC_OK));
}
@TestExtension
public static class Foo implements UnprotectedRootAction {
private static Foo getInstance() {
return ExtensionList.lookupSingleton(Foo.class);
}
private String baz;
@CheckForNull
@Override
public String getIconFileName() {
return null;
}
@CheckForNull
@Override
public String getDisplayName() {
return "Pitied Foos";
}
@CheckForNull
@Override
public String getUrlName() {
return "foo";
}
@GET
public void doBar(@QueryParameter String baz) {
this.baz = baz;
}
@GET
public void doIndex(@QueryParameter String baz) {
this.baz = "index: " + baz;
}
}
}
......@@ -50,6 +50,11 @@ THE SOFTWARE.
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>suspicious-request-filter</filter-name>
<filter-class>jenkins.security.SuspiciousRequestFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter>
<filter-name>diagnostic-name-filter</filter-name>
<filter-class>org.kohsuke.stapler.DiagnosticThreadNameFilter</filter-class>
......@@ -125,7 +130,11 @@ THE SOFTWARE.
<url-pattern>*.png</url-pattern>
</filter-mapping>
-->
<filter-mapping>
<filter-name>suspicious-request-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>diagnostic-name-filter</filter-name>
<url-pattern>/*</url-pattern>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册