这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战
在 Spring Security 中,提供了一个 HttpFirewall 接口,从它的名字可以看出是作为防火墙使用,其定义如下:
public interface HttpFirewall {
FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException;
HttpServletResponse getFirewalledResponse(HttpServletResponse response);
}
可以在 Request 和 Response 对象通过过滤器链之前,对他们进行一些校验,完成安全功能。Spring Security 提供了两个实现类:
其中,DefaultHttpFirewall 比 StrictHttpFirewall 的规则更宽松一些,Spring Security 默认使用的是 StrictHttpFirewall。
StrictHttpFirewall 主要提供了一下这些功能。
一、只允许规定范围内的请求方法。可以通过 StrictHttpFirewall 中的一个方法的源代码看到这些请求方法:
private static Set<String> createDefaultAllowedHttpMethods() {
Set<String> result = new HashSet<>();
result.add(HttpMethod.DELETE.name());
result.add(HttpMethod.GET.name());
result.add(HttpMethod.HEAD.name());
result.add(HttpMethod.OPTIONS.name());
result.add(HttpMethod.PATCH.name());
result.add(HttpMethod.POST.name());
result.add(HttpMethod.PUT.name());
return result;
}
除了这些请求方法之外,通过其他的请求方法发送请求会被拒绝(抛出异常)。
二、请求的 URL 必须是标准华的 URL,从下面这段源码可以找到具体的规则:
private static boolean isNormalized(HttpServletRequest request) {
if (!isNormalized(request.getRequestURI())) {
return false;
}
if (!isNormalized(request.getContextPath())) {
return false;
}
if (!isNormalized(request.getServletPath())) {
return false;
}
if (!isNormalized(request.getPathInfo())) {
return false;
}
return true;
}
这里限制了,请求 URI、上下文路径、Servlet路径、PathInfo 中,都不能包涵 "./", "/../" 或者 "/." 。
三、URI 中只能包含可打印的 ASCII 字符,从以下的源码中可以看出:
private static boolean containsOnlyPrintableAsciiCharacters(String uri) {
int length = uri.length();
for (int i = 0; i < length; i++) {
char ch = uri.charAt(i);
if (ch < '\u0020' || ch > '\u007e') {
return false;
}
}
return true;
}
四、 StrictHttpFirewall 规定了一些不允许在 URL 中不能出现的字符:
- URL 中不能包含分号,包括 URL 编码之后的分号,也就是 ";", "%3b", "%3B" 都不能包含。
- URL 中不允许包含编码后的斜线,也就是
"%2f", "%2F"这两个。 - URL 中不允许包含反斜线,以及编码后的反斜线,也就是
"\", "%5c", "%5C"这三个。 - URL 中不允许包含空字符,也就是
"\0", "%00"这两个。 - URL 中不允许出现编码后的白分号,也就是
"%25"。
五、 StrictHttpFirewall 开规定了一些不允许出现的 Header 名称、Header 值、请求参数名称等,默认都是根据下面这个表达式进行匹配:
private static final Pattern ASSIGNED_AND_NOT_ISO_CONTROL_PATTERN = Pattern
.compile("[\p{IsAssigned}&&[^\p{IsControl}]]*");
总结:
Spring Security 虽然被 Shiro 之类的框架更复杂和重量级,但它也提供了更多的功能,比如以上介绍的防火墙配置,这些特性可以让我们的应用在引入 Spring Security 的时候,就具备了基础的安全保障。