Skip to content

Commit e4f7f68

Browse files
committed
XWIKI-19994: False positive redirect checking
1 parent 00af08f commit e4f7f68

File tree

2 files changed

+131
-1
lines changed

2 files changed

+131
-1
lines changed

Diff for: xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/XWikiServletResponse.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
public class XWikiServletResponse implements XWikiResponse
3939
{
4040
private static final Logger LOGGER = LoggerFactory.getLogger(XWikiServletResponse.class);
41-
private static final Pattern ABSOLUTE_URL_PATTERN = Pattern.compile("[a-z0-9]+://.*");
41+
private static final Pattern ABSOLUTE_URL_PATTERN = Pattern.compile("[a-z0-9]+:/[/]?.*");
4242

4343
private HttpServletResponse response;
4444

@@ -71,6 +71,13 @@ public void sendRedirect(String redirect) throws IOException
7171
return;
7272
}
7373

74+
if (StringUtils.startsWith(redirect, "//")) {
75+
LOGGER.warn("Possible phishing attack, attempting to redirect to [{}]. If this request is legitimate, "
76+
+ "use an actual absolute URL and pay attention to configure properly url.trustedDomains in "
77+
+ "xwiki.properties", redirect);
78+
return;
79+
}
80+
7481
// check for trusted domains, only if the given location is an absolute URL.
7582
if (ABSOLUTE_URL_PATTERN.matcher(redirect).matches()) {
7683
if (!getURLSecurityManager().isDomainTrusted(new URL(redirect))) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* See the NOTICE file distributed with this work for additional
3+
* information regarding copyright ownership.
4+
*
5+
* This is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU Lesser General Public License as
7+
* published by the Free Software Foundation; either version 2.1 of
8+
* the License, or (at your option) any later version.
9+
*
10+
* This software is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this software; if not, write to the Free
17+
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18+
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19+
*/
20+
package com.xpn.xwiki.web;
21+
22+
import java.io.IOException;
23+
import java.net.URL;
24+
25+
import javax.servlet.http.HttpServletResponse;
26+
27+
import org.apache.ecs.wml.U;
28+
import org.junit.jupiter.api.BeforeEach;
29+
import org.junit.jupiter.api.Test;
30+
import org.xwiki.component.manager.ComponentManager;
31+
import org.xwiki.test.annotation.BeforeComponent;
32+
import org.xwiki.test.junit5.mockito.ComponentTest;
33+
import org.xwiki.test.junit5.mockito.InjectMockComponents;
34+
import org.xwiki.test.junit5.mockito.MockComponent;
35+
import org.xwiki.test.mockito.MockitoComponentManager;
36+
import org.xwiki.url.URLSecurityManager;
37+
38+
import static org.junit.jupiter.api.Assertions.*;
39+
import static org.mockito.ArgumentMatchers.any;
40+
import static org.mockito.Mockito.mock;
41+
import static org.mockito.Mockito.never;
42+
import static org.mockito.Mockito.verify;
43+
import static org.mockito.Mockito.when;
44+
45+
/**
46+
* Tests for {@link XWikiServletResponse}.
47+
*
48+
* @version $Id$
49+
*/
50+
@ComponentTest
51+
class XWikiServletResponseTest
52+
{
53+
@MockComponent
54+
private URLSecurityManager urlSecurityManager;
55+
56+
private XWikiServletResponse servletResponse;
57+
private HttpServletResponse httpServletResponse;
58+
59+
@BeforeComponent
60+
void beforeComponent(MockitoComponentManager mockitoComponentManager) throws Exception
61+
{
62+
mockitoComponentManager.registerComponent(ComponentManager.class, "context", mockitoComponentManager);
63+
Utils.setComponentManager(mockitoComponentManager);
64+
}
65+
66+
@BeforeEach
67+
void setup()
68+
{
69+
this.httpServletResponse = mock(HttpServletResponse.class);
70+
this.servletResponse = new XWikiServletResponse(this.httpServletResponse);
71+
}
72+
73+
@Test
74+
void sendRedirect() throws IOException
75+
{
76+
this.servletResponse.sendRedirect("");
77+
verify(this.httpServletResponse, never()).sendRedirect(any());
78+
79+
this.servletResponse.sendRedirect("/xwiki/\n/something/");
80+
verify(this.httpServletResponse, never()).sendRedirect(any());
81+
82+
this.servletResponse.sendRedirect("//xwiki.org/xwiki/something/");
83+
verify(this.httpServletResponse, never()).sendRedirect(any());
84+
85+
String redirect = "http://xwiki.org/xwiki/something/";
86+
URL redirectUrl = new URL(redirect);
87+
when(this.urlSecurityManager.isDomainTrusted(redirectUrl)).thenReturn(false);
88+
this.servletResponse.sendRedirect(redirect);
89+
verify(this.httpServletResponse, never()).sendRedirect(any());
90+
verify(this.urlSecurityManager).isDomainTrusted(redirectUrl);
91+
92+
redirect = "http:/xwiki.com/xwiki/something/";
93+
redirectUrl = new URL(redirect);
94+
when(this.urlSecurityManager.isDomainTrusted(redirectUrl)).thenReturn(false);
95+
this.servletResponse.sendRedirect(redirect);
96+
verify(this.httpServletResponse, never()).sendRedirect(any());
97+
verify(this.urlSecurityManager).isDomainTrusted(redirectUrl);
98+
99+
redirect = "https://floo";
100+
redirectUrl = new URL(redirect);
101+
when(this.urlSecurityManager.isDomainTrusted(redirectUrl)).thenReturn(false);
102+
this.servletResponse.sendRedirect(redirect);
103+
verify(this.httpServletResponse, never()).sendRedirect(any());
104+
verify(this.urlSecurityManager).isDomainTrusted(redirectUrl);
105+
106+
redirect = "ftp://xwiki.org/xwiki/something/";
107+
redirectUrl = new URL(redirect);
108+
when(this.urlSecurityManager.isDomainTrusted(redirectUrl)).thenReturn(false);
109+
this.servletResponse.sendRedirect(redirect);
110+
verify(this.httpServletResponse, never()).sendRedirect(any());
111+
verify(this.urlSecurityManager).isDomainTrusted(redirectUrl);
112+
113+
this.servletResponse.sendRedirect("/xwiki/something/");
114+
verify(this.httpServletResponse).sendRedirect("/xwiki/something/");
115+
116+
redirect = "http://xwiki.org/foo/";
117+
redirectUrl = new URL(redirect);
118+
when(this.urlSecurityManager.isDomainTrusted(redirectUrl)).thenReturn(true);
119+
this.servletResponse.sendRedirect(redirect);
120+
verify(this.httpServletResponse).sendRedirect(redirect);
121+
verify(this.urlSecurityManager).isDomainTrusted(redirectUrl);
122+
}
123+
}

0 commit comments

Comments
 (0)