/* * 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 SEMICOLON = Collections.unmodifiableList(Arrays.asList(";", "%3b", "%3B")); private static final List 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; } }