1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.shiro.web.filter;
21
22 import org.apache.shiro.util.StringUtils;
23 import org.apache.shiro.web.util.WebUtils;
24
25 import javax.servlet.ServletRequest;
26 import javax.servlet.ServletResponse;
27 import javax.servlet.http.HttpServletRequest;
28 import java.util.Arrays;
29 import java.util.Collections;
30 import java.util.List;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 public class InvalidRequestFilter extends AccessControlFilter {
46
47 private static final List<String> SEMICOLON = Collections.unmodifiableList(Arrays.asList(";", "%3b", "%3B"));
48
49 private static final List<String> BACKSLASH = Collections.unmodifiableList(Arrays.asList("\\", "%5c", "%5C"));
50
51 private boolean blockSemicolon = true;
52
53 private boolean blockBackslash = !Boolean.getBoolean(WebUtils.ALLOW_BACKSLASH);
54
55 private boolean blockNonAscii = true;
56
57 @Override
58 protected boolean isAccessAllowed(ServletRequest req, ServletResponse response, Object mappedValue) throws Exception {
59 HttpServletRequest request = WebUtils.toHttp(req);
60
61 return isValid(request.getRequestURI())
62 && isValid(request.getServletPath())
63 && isValid(request.getPathInfo());
64 }
65
66 private boolean isValid(String uri) {
67 return !StringUtils.hasText(uri)
68 || ( !containsSemicolon(uri)
69 && !containsBackslash(uri)
70 && !containsNonAsciiCharacters(uri));
71 }
72
73 @Override
74 protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
75 WebUtils.toHttp(response).sendError(400, "Invalid request");
76 return false;
77 }
78
79 private boolean containsSemicolon(String uri) {
80 if (isBlockSemicolon()) {
81 return SEMICOLON.stream().anyMatch(uri::contains);
82 }
83 return false;
84 }
85
86 private boolean containsBackslash(String uri) {
87 if (isBlockBackslash()) {
88 return BACKSLASH.stream().anyMatch(uri::contains);
89 }
90 return false;
91 }
92
93 private boolean containsNonAsciiCharacters(String uri) {
94 if (isBlockNonAscii()) {
95 return !containsOnlyPrintableAsciiCharacters(uri);
96 }
97 return false;
98 }
99
100 private static boolean containsOnlyPrintableAsciiCharacters(String uri) {
101 int length = uri.length();
102 for (int i = 0; i < length; i++) {
103 char c = uri.charAt(i);
104 if (c < '\u0020' || c > '\u007e') {
105 return false;
106 }
107 }
108 return true;
109 }
110
111 public boolean isBlockSemicolon() {
112 return blockSemicolon;
113 }
114
115 public void setBlockSemicolon(boolean blockSemicolon) {
116 this.blockSemicolon = blockSemicolon;
117 }
118
119 public boolean isBlockBackslash() {
120 return blockBackslash;
121 }
122
123 public void setBlockBackslash(boolean blockBackslash) {
124 this.blockBackslash = blockBackslash;
125 }
126
127 public boolean isBlockNonAscii() {
128 return blockNonAscii;
129 }
130
131 public void setBlockNonAscii(boolean blockNonAscii) {
132 this.blockNonAscii = blockNonAscii;
133 }
134 }