对比新文件 |
| | |
| | | /* |
| | | * Licensed to the Apache Software Foundation (ASF) under one |
| | | * or more contributor license agreements. See the NOTICE file |
| | | * distributed with this work for additional information |
| | | * regarding copyright ownership. The ASF licenses this file |
| | | * to you under the Apache License, Version 2.0 (the |
| | | * "License"); you may not use this file except in compliance |
| | | * with the License. You may obtain a copy of the License at |
| | | * |
| | | * http://www.apache.org/licenses/LICENSE-2.0 |
| | | * |
| | | * Unless required by applicable law or agreed to in writing, |
| | | * software distributed under the License is distributed on an |
| | | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| | | * KIND, either express or implied. See the License for the |
| | | * specific language governing permissions and limitations |
| | | * under the License. |
| | | */ |
| | | |
| | | package org.apache.shiro.web.filter; |
| | | |
| | | import org.apache.shiro.web.util.WebUtils; |
| | | |
| | | import javax.servlet.ServletRequest; |
| | | import javax.servlet.ServletResponse; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * A request filter that blocks malicious requests. Invalid request will respond with a 400 response code. |
| | | * |
| | | |
| | | |
| | | * This filter checks and blocks the request if the following characters are found in the request URI: |
| | | * |
| | | |
| | | |
| | | * |
| | | Semicolon - can be disabled by setting {@code blockSemicolon = false} |
| | | |
| | | * |
| | | Backslash - can be disabled by setting {@code blockBackslash = false} |
| | | |
| | | * |
| | | Non-ASCII characters - can be disabled by setting {@code blockNonAscii = false}, the ability to disable this check will be removed in future version. |
| | | |
| | | * |
| | | |
| | | |
| | | * |
| | | * @see class was inspired by Spring Security StrictHttpFirewall |
| | | * @since 1.6 |
| | | */ |
| | | public class InvalidRequestFilter extends AccessControlFilter { |
| | | |
| | | private static final List<String> SEMICOLON = Collections.unmodifiableList(Arrays.asList(";", "%3b", "%3B")); |
| | | |
| | | private static final List<String> BACKSLASH = Collections.unmodifiableList(Arrays.asList("\\", "%5c", "%5C")); |
| | | |
| | | private boolean blockSemicolon = true; |
| | | |
| | | private boolean blockBackslash = true; |
| | | |
| | | private boolean blockNonAscii = true; |
| | | |
| | | @Override |
| | | protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { |
| | | String uri = WebUtils.toHttp(request).getRequestURI(); |
| | | return !containsSemicolon(request,uri) |
| | | && !containsBackslash(uri) |
| | | && !containsNonAsciiCharacters(uri); |
| | | } |
| | | |
| | | @Override |
| | | protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { |
| | | String uri = WebUtils.toHttp(request).getRequestURI(); |
| | | WebUtils.toHttp(response).sendError(400, "Invalid request"); |
| | | return false; |
| | | } |
| | | |
| | | private String ctx=null; |
| | | private boolean containsSemicolon(ServletRequest request,String uri) { |
| | | if (isBlockSemicolon()) { |
| | | if(ctx == null) { |
| | | ctx = WebUtils.toHttp(request).getContextPath(); |
| | | } |
| | | // 登录url拼接的jsessionId进行放行 |
| | | if(uri.startsWith(ctx + this.getLoginUrl() + ";jsessionid=") || |
| | | uri.startsWith(ctx + this.getLoginUrl() + "%3bjsessionid=") || |
| | | uri.startsWith(ctx + this.getLoginUrl() + "%3Bjsessionid=") || |
| | | uri.startsWith(ctx + "/javax.faces.resource/mybootstrap.css.xhtml;jsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/mybootstrap.css.xhtml%3bjsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/mybootstrap.css.xhtml%3Bjsessionid=")|| |
| | | |
| | | uri.startsWith(ctx + "/javax.faces.resource/default.css.xhtml;jsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/default.css.xhtml%3bjsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/default.css.xhtml%3Bjsessionid=")|| |
| | | |
| | | uri.startsWith(ctx + "/javax.faces.resource/components.css.xhtml;jsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/components.css.xhtml%3bjsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/components.css.xhtml%3Bjsessionid=")|| |
| | | |
| | | uri.startsWith(ctx + "/javax.faces.resource/jquery/jquery-plugins.js.xhtml;jsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/jquery/jquery-plugins.js.xhtml%3bjsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/jquery/jquery-plugins.js.xhtml%3Bjsessionid=")|| |
| | | |
| | | uri.startsWith(ctx + "/javax.faces.resource/jquery/jquery.js.xhtml;jsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/jquery/jquery.js.xhtml%3bjsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/jquery/jquery.js.xhtml%3Bjsessionid=")|| |
| | | |
| | | uri.startsWith(ctx + "/javax.faces.resource/core.js.xhtml;jsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/core.js.xhtml%3bjsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/core.js.xhtml%3Bjsessionid=")|| |
| | | |
| | | uri.startsWith(ctx + "/javax.faces.resource/extra.js.xhtml;jsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/extra.js.xhtml%3bjsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/extra.js.xhtml%3Bjsessionid=")|| |
| | | |
| | | uri.startsWith(ctx + "/resources/images/logo1.png;jsessionid=")|| |
| | | uri.startsWith(ctx + "/resources/images/logo1.png%3bjsessionid=")|| |
| | | uri.startsWith(ctx + "/resources/images/logo1.png%3Bjsessionid=")|| |
| | | |
| | | uri.startsWith(ctx + "/javax.faces.resource/components.js.xhtml;jsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/components.js.xhtml%3bjsessionid=")|| |
| | | uri.startsWith(ctx + "/javax.faces.resource/components.js.xhtml%3Bjsessionid=")|| |
| | | |
| | | uri.startsWith(ctx + "/resources/images/logo.png;jsessionid=")|| |
| | | uri.startsWith(ctx + "/resources/images/logo.png%3bjsessionid=")|| |
| | | uri.startsWith(ctx + "/resources/images/logo.png%3Bjsessionid=")) { |
| | | return false; |
| | | } |
| | | return SEMICOLON.stream().anyMatch(uri::contains); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private boolean containsBackslash(String uri) { |
| | | if (isBlockBackslash()) { |
| | | return BACKSLASH.stream().anyMatch(uri::contains); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private boolean containsNonAsciiCharacters(String uri) { |
| | | if (isBlockNonAscii()) { |
| | | return !containsOnlyPrintableAsciiCharacters(uri); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private static boolean containsOnlyPrintableAsciiCharacters(String uri) { |
| | | int length = uri.length(); |
| | | for (int i = 0; i < length; i++) { |
| | | char c = uri.charAt(i); |
| | | if (c < '\u0020' || c > '\u007e') { |
| | | return false; |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | public boolean isBlockSemicolon() { |
| | | return blockSemicolon; |
| | | } |
| | | |
| | | public void setBlockSemicolon(boolean blockSemicolon) { |
| | | this.blockSemicolon = blockSemicolon; |
| | | } |
| | | |
| | | public boolean isBlockBackslash() { |
| | | return blockBackslash; |
| | | } |
| | | |
| | | public void setBlockBackslash(boolean blockBackslash) { |
| | | this.blockBackslash = blockBackslash; |
| | | } |
| | | |
| | | public boolean isBlockNonAscii() { |
| | | return blockNonAscii; |
| | | } |
| | | |
| | | public void setBlockNonAscii(boolean blockNonAscii) { |
| | | this.blockNonAscii = blockNonAscii; |
| | | } |
| | | } |