Skip to content

Commit

Permalink
JCR-3909: CSRF bug in Jackrabbit-Webdav (ported to 2.10)
Browse files Browse the repository at this point in the history
git-svn-id: https://svn.apache.org/repos/asf/jackrabbit/branches/2.10@1758753 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
reschke committed Sep 1, 2016
1 parent f77b97e commit 4108e9f
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 44 deletions.
Expand Up @@ -16,12 +16,14 @@
*/
package org.apache.jackrabbit.webdav.util;

import org.apache.jackrabbit.webdav.DavMethods;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
Expand All @@ -36,6 +38,19 @@ public class CSRFUtil {
*/
public static final String DISABLED = "disabled";

/**
* Request content types for CSRF checking, see JCR-3909
*/
public static final Set<String> CONTENT_TYPES = Collections.unmodifiableSet(new HashSet<String>(
Arrays.asList(
new String[] {
"application/x-www-form-urlencoded",
"multipart/form-data",
"text/plain"
}
)
));

/**
* logger instance
*/
Expand Down Expand Up @@ -93,19 +108,21 @@ public CSRFUtil(String config) {
}

public boolean isValidRequest(HttpServletRequest request) throws MalformedURLException {
if (disabled) {
int methodCode = DavMethods.getMethodCode(request.getMethod());
if (disabled || DavMethods.DAV_POST != methodCode || !CONTENT_TYPES.contains(request.getContentType())) {
return true;
} else {

String refHeader = request.getHeader("Referer");
// empty referrer headers are not allowed for POST + relevant content types (see JCR-3909)
if (refHeader == null) {
// empty referrer is always allowed
return true;
} else {
String host = new URL(refHeader).getHost();
// test referrer-host equelst server or
// if it is contained in the set of explicitly allowed host names
return host.equals(request.getServerName()) || allowedReferrerHosts.contains(host);
return false;
}

String host = new URL(refHeader).getHost();
// test referrer-host equals server or
// if it is contained in the set of explicitly allowed host names
return host.equals(request.getServerName()) || allowedReferrerHosts.contains(host);
}
}
}
Expand Up @@ -29,11 +29,14 @@
import java.net.MalformedURLException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

/**
* <code>CSRFUtilTest</code>...
Expand All @@ -42,64 +45,77 @@ public class CSRFUtilTest extends TestCase {

private static final String SERVER_NAME = "localhost";

private static final String GET = "GET";
private static final String POST = "POST";

private static final List<String> validURLs = new ArrayList<String>();
private static final List<String> invalidURLs = new ArrayList<String>();

static {
validURLs.add(null);
validURLs.add("http://localhost:4503/jackrabbit/server");
validURLs.add("https://localhost:4503/jackrabbit/server");
validURLs.add("https://localhost/jackrabbit/server");

invalidURLs.add("http://invalidHost/test");
invalidURLs.add("http://host1:8080/test");
invalidURLs.add("http://user:pw@host2/test");
}

private static void testValid(CSRFUtil util, Collection<String> validURLs) throws MalformedURLException {
for (String url : validURLs) {
assertTrue(url, util.isValidRequest(createRequest(url)));
private static void testValid(CSRFUtil util, Collection<String> validURLs, String method, Set<String> contentTypes) throws MalformedURLException {
if (null == contentTypes) {
for (String url : validURLs) {
assertTrue(url, util.isValidRequest(createRequest(url, method, null)));
}
} else {
for (String contentType : contentTypes) {
for (String url : validURLs) {
assertTrue(url, util.isValidRequest(createRequest(url, method, contentType)));
}
}
}
}

private static void testInvalid(CSRFUtil util, Collection<String> invalidURLs) throws MalformedURLException {
for (String url : invalidURLs) {
assertFalse(url, util.isValidRequest(createRequest(url)));
private static void testInvalid(CSRFUtil util, Collection<String> invalidURLs, String method, Set<String> contentTypes) throws MalformedURLException {
if (null == contentTypes) {
for (String url : validURLs) {
assertFalse(url, util.isValidRequest(createRequest(url, method, null)));
}
} else {
for (String contentType : contentTypes) {
for (String url : invalidURLs) {
assertFalse(url, util.isValidRequest(createRequest(url, method, contentType)));
}
}
}
}

private static HttpServletRequest createRequest(String url) {
return new DummyRequest(url, SERVER_NAME);
private static HttpServletRequest createRequest(String url, String method, String contentType) {
return new DummyRequest(url, SERVER_NAME, method, contentType);
}

public void testNullConfig() throws Exception {
CSRFUtil util = new CSRFUtil(null);

testValid(util, validURLs);

List<String> invalidURLs = new ArrayList<String>();
invalidURLs.add("http://invalidHost/test");
invalidURLs.add("http://host1:8080/test");
invalidURLs.add("http://user:pw@host2/test");
testInvalid(util, invalidURLs);
testValid(util, validURLs, POST, CSRFUtil.CONTENT_TYPES);
testInvalid(util, invalidURLs, POST, CSRFUtil.CONTENT_TYPES);
}

public void testEmptyConfig() throws Exception {
CSRFUtil util = new CSRFUtil("");
testValid(util, validURLs);
testValid(util, validURLs, POST, CSRFUtil.CONTENT_TYPES);
testInvalid(util, invalidURLs, POST, CSRFUtil.CONTENT_TYPES);
}

List<String> invalidURLs = new ArrayList<String>();
invalidURLs.add("http://invalidHost/test");
invalidURLs.add("http://host1:8080/test");
invalidURLs.add("http://user:pw@host2/test");
testInvalid(util, invalidURLs);
public void testNoReferrer() throws Exception {
CSRFUtil util = new CSRFUtil("");
testValid(util, validURLs, POST, CSRFUtil.CONTENT_TYPES);
assertFalse("no referrer", util.isValidRequest(createRequest(null, POST, "text/plain")));
}

public void testDisabledConfig() throws Exception {
CSRFUtil util = new CSRFUtil(CSRFUtil.DISABLED);
testValid(util, validURLs);

testValid(util, validURLs, POST, CSRFUtil.CONTENT_TYPES);
// since test is disabled any other referer host must be allowed
List<String> otherHosts = new ArrayList<String>();
otherHosts.add("http://validHost:80/test");
otherHosts.add("http://host1/test");
otherHosts.add("https://user:pw@host2/test");
testValid(util, otherHosts);
testValid(util, invalidURLs, POST, CSRFUtil.CONTENT_TYPES);
}

public void testConfig() throws Exception {
Expand All @@ -121,20 +137,31 @@ public void testConfig() throws Exception {

for (String config : configs) {
CSRFUtil util = new CSRFUtil(config);
testValid(util, validURLs);
testValid(util, otherHosts);
testInvalid(util, invalidURLs);
testValid(util, validURLs, POST, CSRFUtil.CONTENT_TYPES);
testValid(util, otherHosts, POST, CSRFUtil.CONTENT_TYPES);
testInvalid(util, invalidURLs, POST, CSRFUtil.CONTENT_TYPES);
}
}

public void testMethodsAndMediaType() throws Exception {
CSRFUtil util = new CSRFUtil("");
testValid(util, invalidURLs, GET, CSRFUtil.CONTENT_TYPES);
testValid(util, invalidURLs, POST, new HashSet<String>(Arrays.asList(new String[] {"application/json"})));
testInvalid(util, invalidURLs, POST, CSRFUtil.CONTENT_TYPES);
}

private static final class DummyRequest implements HttpServletRequest {

private final String referer;
private final String serverName;
private final String method;
private final String contentType;

private DummyRequest(String referer, String serverName) {
private DummyRequest(String referer, String serverName, String method, String contentType) {
this.referer = referer;
this.serverName = serverName;
this.method = method;
this.contentType = contentType;
}

//---------------------------------------------< HttpServletRequest >---
Expand Down Expand Up @@ -171,7 +198,7 @@ public int getIntHeader(String name) {
return 0;
}
public String getMethod() {
return null;
return method;
}
public String getPathInfo() {
return null;
Expand Down Expand Up @@ -240,7 +267,7 @@ public int getContentLength() {
return 0;
}
public String getContentType() {
return null;
return contentType;
}
public ServletInputStream getInputStream() throws IOException {
return null;
Expand Down

0 comments on commit 4108e9f

Please sign in to comment.