Skip to content

Commit 0f472b7

Browse files
committed
- restored backwards compatibility
- using URLDecode() before patch matching to be even more secure by default - allow unencoded semicolons by default
1 parent 1845332 commit 0f472b7

File tree

4 files changed

+55
-22
lines changed

4 files changed

+55
-22
lines changed

web/src/main/java/org/apache/shiro/web/filter/InvalidRequestFilter.java

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,13 @@
4646
* @since 1.6
4747
*/
4848
public class InvalidRequestFilter extends AccessControlFilter {
49-
50-
enum PathTraversalBlockMode {
49+
public enum PathTraversalBlockMode {
5150
STRICT,
5251
NORMAL,
5352
NO_BLOCK;
5453
}
5554

56-
private static final List<String> SEMICOLON = Collections.unmodifiableList(Arrays.asList(";", "%3b", "%3B"));
55+
private static final List<String> SEMICOLON = Collections.unmodifiableList(Arrays.asList("%3b", "%3B"));
5756

5857
private static final List<String> BACKSLASH = Collections.unmodifiableList(Arrays.asList("\\", "%5c", "%5C"));
5958

@@ -98,7 +97,8 @@ protected boolean onAccessDenied(ServletRequest request, ServletResponse respons
9897

9998
private boolean containsSemicolon(String uri) {
10099
if (isBlockSemicolon()) {
101-
return SEMICOLON.stream().anyMatch(uri::contains);
100+
return SEMICOLON.stream().anyMatch(uri::contains)
101+
|| (pathTraversalBlockMode == PathTraversalBlockMode.STRICT && uri.contains(";"));
102102
}
103103
return false;
104104
}
@@ -129,10 +129,10 @@ private static boolean containsOnlyPrintableAsciiCharacters(String uri) {
129129
}
130130

131131
private boolean containsTraversal(String uri) {
132-
if (isBlockTraversalNormal()) {
132+
if (pathTraversalBlockMode == PathTraversalBlockMode.NORMAL) {
133133
return !(isNormalized(uri));
134134
}
135-
if (isBlockTraversalStrict()) {
135+
if (pathTraversalBlockMode == PathTraversalBlockMode.STRICT) {
136136
return !(isNormalized(uri)
137137
&& PERIOD.stream().noneMatch(uri::contains)
138138
&& FORWARDSLASH.stream().noneMatch(uri::contains));
@@ -190,21 +190,49 @@ public void setBlockNonAscii(boolean blockNonAscii) {
190190
this.blockNonAscii = blockNonAscii;
191191
}
192192

193-
public boolean isBlockTraversalNormal() {
194-
return pathTraversalBlockMode == PathTraversalBlockMode.NORMAL;
193+
public PathTraversalBlockMode getPathTraversalBlockMode() {
194+
return pathTraversalBlockMode;
195+
}
196+
197+
public void setBlockPathTraversal(PathTraversalBlockMode mode) {
198+
this.pathTraversalBlockMode = mode;
195199
}
196200

197-
public boolean isBlockTraversalStrict() {
201+
public boolean isBlockEncodedPeriod() {
198202
return pathTraversalBlockMode == PathTraversalBlockMode.STRICT;
199203
}
200204

201-
public void setPathTraversalBlockMode(PathTraversalBlockMode mode) {
202-
this.pathTraversalBlockMode = mode;
205+
public void setBlockEncodedPeriod(boolean blockEncodedPeriod) {
206+
setBlockPathTraversal(blockEncodedPeriod ? PathTraversalBlockMode.STRICT : PathTraversalBlockMode.NORMAL);
207+
}
208+
209+
public boolean isBlockEncodedForwardSlash() {
210+
return pathTraversalBlockMode == PathTraversalBlockMode.STRICT;
211+
}
212+
213+
public void setBlockEncodedForwardSlash(boolean blockEncodedForwardSlash) {
214+
setBlockPathTraversal(blockEncodedForwardSlash ? PathTraversalBlockMode.STRICT : PathTraversalBlockMode.NORMAL);
215+
}
216+
217+
public boolean isBlockRewriteTraversal() {
218+
return pathTraversalBlockMode == PathTraversalBlockMode.NORMAL;
219+
}
220+
221+
public void setBlockRewriteTraversal(boolean blockRewriteTraversal) {
222+
setBlockPathTraversal(blockRewriteTraversal ? PathTraversalBlockMode.NORMAL : PathTraversalBlockMode.NO_BLOCK);
223+
}
224+
225+
/**
226+
* @deprecated use {@link #getPathTraversalBlockMode()} instead
227+
*/
228+
@Deprecated
229+
public boolean isBlockTraversal() {
230+
return pathTraversalBlockMode != PathTraversalBlockMode.NO_BLOCK;
203231
}
204232

205233
/**
206234
*
207-
* @deprecated Use {@link #setPathTraversalBlockMode(PathTraversalBlockMode)}
235+
* @deprecated Use {@link #setBlockPathTraversal(PathTraversalBlockMode)}
208236
*/
209237
@Deprecated
210238
public void setBlockTraversal(boolean blockTraversal) {

web/src/main/java/org/apache/shiro/web/util/WebUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ private WebUtils() {
116116
* @return the path within the web application
117117
*/
118118
public static String getPathWithinApplication(HttpServletRequest request) {
119-
return normalize(removeSemicolon(getServletPath(request) + getPathInfo(request)));
119+
return normalize(decodeAndCleanUriString(request, getServletPath(request) + getPathInfo(request)));
120120
}
121121

122122
/**

web/src/test/groovy/org/apache/shiro/web/filter/InvalidRequestFilterTest.groovy

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class InvalidRequestFilterTest {
3939
assertThat "filter.blockBackslash expected to be true", filter.isBlockBackslash()
4040
assertThat "filter.blockNonAscii expected to be true", filter.isBlockNonAscii()
4141
assertThat "filter.blockSemicolon expected to be true", filter.isBlockSemicolon()
42-
assertThat "filter.blockTraversal expected to be NORMAL", filter.isBlockTraversalNormal()
42+
assertThat "filter.blockTraversal expected to be NORMAL",
43+
filter.getPathTraversalBlockMode() == InvalidRequestFilter.PathTraversalBlockMode.NORMAL
4344
}
4445

4546
@Test
@@ -67,13 +68,9 @@ class InvalidRequestFilterTest {
6768
assertPathBlocked(filter, "/\\something")
6869
assertPathBlocked(filter, "/%5csomething")
6970
assertPathBlocked(filter, "/%5Csomething")
70-
assertPathBlocked(filter, "/;something")
7171
assertPathBlocked(filter, "/%3bsomething")
7272
assertPathBlocked(filter, "/%3Bsomething")
7373
assertPathBlocked(filter, "/\u0019something")
74-
75-
assertPathBlocked(filter, "/something", "/;something")
76-
assertPathBlocked(filter, "/something", "/something", "/;")
7774
}
7875

7976
@Test
@@ -101,12 +98,14 @@ class InvalidRequestFilterTest {
10198
assertPathAllowed(filter, "/something/http:%2f%2fmydomain.example.com%2foidc/bar/")
10299
assertPathAllowed(filter, "/something/%2e%2E/bar/")
103100
assertPathAllowed(filter, "/something/http:%2f%2fmydomain%2eexample%2ecom%2foidc/bar/")
101+
assertPathAllowed(filter, "/;something")
102+
assertPathAllowed(filter, "/something", "/;something")
104103
}
105104

106105
@Test
107106
void testBlocksTraversalStrict() {
108107
InvalidRequestFilter filter = new InvalidRequestFilter()
109-
filter.setPathTraversalBlockMode(InvalidRequestFilter.PathTraversalBlockMode.STRICT)
108+
filter.setBlockPathTraversal(InvalidRequestFilter.PathTraversalBlockMode.STRICT)
110109
assertPathBlocked(filter, "/something/../")
111110
assertPathBlocked(filter, "/something/../bar")
112111
assertPathBlocked(filter, "/something/../bar/")
@@ -129,6 +128,9 @@ class InvalidRequestFilterTest {
129128
assertPathBlocked(filter, "/something/http:%2f%2fmydomain.example.com%2foidc/bar/")
130129
assertPathBlocked(filter, "/something/%2e%2E/bar/")
131130
assertPathBlocked(filter, "/something/http:%2f%2fmydomain%2eexample%2ecom%2foidc/bar/")
131+
assertPathBlocked(filter, "/;something")
132+
assertPathBlocked(filter, "/something", "/;something")
133+
assertPathBlocked(filter, "/something", "/something", "/;")
132134
}
133135

134136
@Test
@@ -138,7 +140,6 @@ class InvalidRequestFilterTest {
138140
assertPathAllowed(filter, "/\\something")
139141
assertPathAllowed(filter, "/%5csomething")
140142
assertPathAllowed(filter, "/%5Csomething")
141-
assertPathBlocked(filter, "/;something")
142143
assertPathBlocked(filter, "/%3bsomething")
143144
assertPathBlocked(filter, "/%3Bsomething")
144145
assertPathBlocked(filter, "/\u0019something")
@@ -154,7 +155,6 @@ class InvalidRequestFilterTest {
154155
assertPathBlocked(filter, "/\\something")
155156
assertPathBlocked(filter, "/%5csomething")
156157
assertPathBlocked(filter, "/%5Csomething")
157-
assertPathBlocked(filter, "/;something")
158158
assertPathBlocked(filter, "/%3bsomething")
159159
assertPathBlocked(filter, "/%3Bsomething")
160160
assertPathAllowed(filter, "/\u0019something")
@@ -182,7 +182,7 @@ class InvalidRequestFilterTest {
182182
@Test
183183
void testAllowTraversal() {
184184
InvalidRequestFilter filter = new InvalidRequestFilter()
185-
filter.setPathTraversalBlockMode(InvalidRequestFilter.PathTraversalBlockMode.NO_BLOCK);
185+
filter.setBlockPathTraversal(InvalidRequestFilter.PathTraversalBlockMode.NO_BLOCK);
186186

187187
assertPathAllowed(filter, "/something/../")
188188
assertPathAllowed(filter, "/something/../bar")
@@ -207,6 +207,9 @@ class InvalidRequestFilterTest {
207207
assertPathAllowed(filter, "/something/http:%2f%2fmydomain.example.com%2foidc/bar/")
208208
assertPathAllowed(filter, "/something/%2e%2E/bar/")
209209
assertPathAllowed(filter, "/something/http:%2f%2fmydomain%2eexample%2ecom%2foidc/bar/")
210+
assertPathAllowed(filter, "/;something")
211+
assertPathAllowed(filter, "/something", "/;something")
212+
assertPathAllowed(filter, "/something", "/something", "/;")
210213
}
211214

212215
static void assertPathBlocked(InvalidRequestFilter filter, String requestUri, String servletPath = requestUri, String pathInfo = null) {

web/src/test/groovy/org/apache/shiro/web/util/WebUtilsTest.groovy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test
2323
import org.junit.jupiter.api.parallel.Isolated
2424

2525
import javax.servlet.http.HttpServletRequest
26+
import java.nio.charset.StandardCharsets
2627

2728
import static org.easymock.EasyMock.*
2829
import static org.hamcrest.MatcherAssert.*
@@ -252,6 +253,7 @@ class WebUtilsTest {
252253
def request = createMock(HttpServletRequest)
253254
expect(request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE)).andReturn(servletPath)
254255
expect(request.getAttribute(WebUtils.INCLUDE_PATH_INFO_ATTRIBUTE)).andReturn(pathInfo)
256+
expect(request.getCharacterEncoding()).andReturn(StandardCharsets.UTF_8.name())
255257
if (pathInfo == null) {
256258
expect(request.getPathInfo()).andReturn(null) // path info can be null
257259
}

0 commit comments

Comments
 (0)