Before, some friends said that Spring Security is so troublesome, why not write a Filter to intercept requests?
Writing your own can also be implemented, but most of the time, you are not a professional Web security engineer, so consider the problem is just authentication and authorization, these two problems handled, it seems that the system is very secure.
That’s not true!
All kinds of Web attacks happen every day, such as fixed session attacks, CSRF attacks, etc., and if you don’t know about these attacks, your system will certainly not be able to defend against them.
The advantage of using Spring Security is that you don’t have to worry about these attacks even if you don’t know about them, because Spring Security has already taken care of your defenses.
We often say that Spring Security is more heavyweight than Shiro, and heavyweight has heavyweight benefits such as full functionality and better Security management. With Spring Security, you have no idea how secure your system is!
Today I’m going to talk to you about the built-in firewall mechanism in Spring Security.
This is the 15th article in the Spring Security series, and reading previous articles in this series will help you understand it better:
- Dig a big hole and Spring Security will do it!
- How to decrypt the password
- A step-by-step guide to customizing form logins in Spring Security
- Spring Security does front and back separation, so don’t do page jumps! All JSON interactions
- Authorization in Spring Security used to be so simple
- How does Spring Security store user data into the database?
- Spring Security+Spring Data Jpa, Security management is only easier!
- Spring Boot + Spring Security enables automatic login
- Spring Boot automatic login. How to control security risks?
- How is Spring Security better than Shiro in microservices projects?
- Two ways for SpringSecurity to customize authentication logic (advanced play)
- How can I quickly view information such as the IP address of the login user in Spring Security?
- Spring Security automatically kicks out the previous login user.
- How can I kick out a user who has logged in to Spring Boot + Vue?
All right, without further ado, let’s read the article.
1.HttpFirewall
Spring Security provides an HttpFirewall, which by its name is a request firewall, that automatically handles illegal requests.
HttpFirewall currently has two implementation classes:
One is the strict firewall setting, and one is the default firewall setting.
DefaultHttpFirewall restrictions relative to StrictHttpFirewall to some loose, of course, also means that security is not as good as StrictHttpFirewall.
StrictHttpFirewall is used by default in Spring Security.
2. Protective measures
So StrictHttpFirewall from what aspects to protect our application? Let’s take a look at each of them.
2.1 Only methods in the whitelist are allowed
First, for requested methods, only whitelisted methods are allowed, that is, not all HTTP request methods can be executed.
This we can see from StrictHttpFirewall source code:
public class StrictHttpFirewall implements HttpFirewall {
private Set<String> allowedHttpMethods = createDefaultAllowedHttpMethods();
private static Set<String> createDefaultAllowedHttpMethods(a) {
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;
}
private void rejectForbiddenHttpMethod(HttpServletRequest request) {
if (this.allowedHttpMethods == ALLOW_ANY_HTTP_METHOD) {
return;
}
if (!this.allowedHttpMethods.contains(request.getMethod())) {
throw new RequestRejectedException("The request was rejected because the HTTP method \"" +
request.getMethod() +
"\" was not included within the whitelist " +
this.allowedHttpMethods); }}}Copy the code
As we can see from this code, your HTTP request method must be DELETE, GET, HEAD, OPTIONS, PATCH, POST, or PUT for the request to be sent successfully. Otherwise, RequestRejectedException is thrown.
What if you want to send other HTTP request methods, such as TRACE? We only need to provide a new StrictHttpFirewall instance ourselves, as follows:
@Bean
HttpFirewall httpFirewall(a) {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setUnsafeAllowAnyHttpMethod(true);
return firewall;
}
Copy the code
Method of setUnsafeAllowAnyHttpMethod said don’t do the HTTP request method validation, namely what method can be. Alternatively, the setAllowedHttpMethods method can be used to redefine the methods that can be passed.
2.2 The requested address cannot contain a semicolon
I don’t know if you’ve tried this, but if you’re using Spring Security, you can’t have the requested address; If the request address has; , it will automatically jump to the following page:
As you can see, the page prompts already say, because your request address contains; , so the request failed.
When the request address is included; ? If you disable cookies, the jsessionID will appear in the address bar like this:
http://localhost:8080/hello; jsessionid=xxCopy the code
This way of passing jsessionids is actually quite insecure (songo will talk more about this in a later article), so it is disabled by default in Spring Security.
Of course, if you want the address bar to be allowed; , you can set it as follows:
@Bean
HttpFirewall httpFirewall(a) {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowSemicolon(true);
return firewall;
}
Copy the code
Error 404 (not allowed); error 404 (not allowed); error 404 (not allowed); error 404 (not allowed); In the wrong.
Notice that in the URL address,;
After the code is%3b
or%3B
, so the address also cannot appear%3b
or%3B
digression
For those of you who may not know it or have not used it, Spring3.2 brings a new way of passing parameters @matrixvariable.
@matrixvariable is a feature of Spring3.2, which extends the format of the request parameter transfer, so that the parameters can be used between; Separation? This is a way of passing people around. Since Spring Security disallows this method by default, you will normally have to pass extra parameters in Spring Security if you need to use @matrixvariable to mark parameters.
So let me show you a simple example of @matrixvariable.
Let’s create a new /hello method:
@RequestMapping(value = "/hello/{id}")
public void hello(@PathVariable Integer id,@MatrixVariable String name) {
System.out.println("id = " + id);
System.out.println("name = " + name);
}
Copy the code
We also need to configure SpringMVC so that; Do not be automatically removed:
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
protected void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false); configurer.setUrlPathHelper(urlPathHelper); }}Copy the code
Then start the project (note that Spring Security has also been configured to allow presence in urls;) , the browser sends the following request:
http://localhost:8080/hello/123; name=javaboyCopy the code
The following information is displayed on the console:
id = 123
name = javaboy
Copy the code
So you can see that the @MatrixVariable annotation is already in effect.
2.3 Must be standardized urls
The requested address must be a standardized URL.
What are normalized urls? Standardized URL is mainly judged from four aspects, we look at the source code:
StrictHttpFirewall# isNormalized:
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;
}
Copy the code
GetRequestURI is to get a character outside the request protocol; GetContextPath is the path to get the context, equivalent to the name of the project; GetServletPath is the requested servlet path, and getPathInfo is the remainder after contextPath and servletPath.
None of the four paths can contain the following strings:
". /"."/.. /" or "/."
Copy the code
2.4 Must be printable ASCII characters
If the request address contains non-printable ASCII characters, the request will be rejected, as can be seen from the source code:
StrictHttpFirewall#containsOnlyPrintableAsciiCharacters
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;
}
Copy the code
2.5 Double slashes are not allowed
If a double slash appears in the request address, the request will also be rejected. “%2F%2F”, “%2F%2F”, “%2F%2F”, “%2F%2F”, “%2F%2F”, “%2F%2F”
If you want the request address to appear //, you can configure it as follows:
@Bean
HttpFirewall httpFirewall(a) {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedDoubleSlash(true);
return firewall;
}
Copy the code
2.6 % is not allowed
If % appears in the requested address, the request will also be rejected. The URL encoded % is %25, so %25 cannot appear in the URL address.
If you want % to appear in the request address, you can change it as follows:
@Bean
HttpFirewall httpFirewall(a) {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedPercent(true);
return firewall;
}
Copy the code
2.7 Forward slashes are not allowed
If the request address contains the slash encoded character %2F or %2F, the request will be rejected.
If the request address contains backslash \ or backslash encoded characters %5C or %5C, the request will be rejected.
To remove the previous two restrictions, perform the following operations:
@Bean
HttpFirewall httpFirewall(a) {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowBackSlash(true);
firewall.setAllowUrlEncodedSlash(true);
return firewall;
}
Copy the code
2.8 .
Not allowed to
If the request address contains the characters %2e, %2e after the. Encoding, the request will be rejected.
If support is required, perform the following operations:
@Bean
HttpFirewall httpFirewall(a) {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedPeriod(true);
return firewall;
}
Copy the code
2.9 summary
It is important to note that these restrictions apply to the requestURI of the request, not to the request parameters. For example, your request format is:
http://localhost:8080/hello?param=aa%2ebb
Copy the code
So the limitations in section 2.7 are none of your business.
This is easy to see from StrictHttpFirewall source code:
public class StrictHttpFirewall implements HttpFirewall {
@Override
public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
rejectForbiddenHttpMethod(request);
rejectedBlacklistedUrls(request);
rejectedUntrustedHosts(request);
if(! isNormalized(request)) {throw new RequestRejectedException("The request was rejected because the URL was not normalized.");
}
String requestUri = request.getRequestURI();
if(! containsOnlyPrintableAsciiCharacters(requestUri)) {throw new RequestRejectedException("The requestURI was rejected because it can only contain printable ASCII characters.");
}
return new FirewalledRequest(request) {
@Override
public void reset(a) {}}; }private void rejectedBlacklistedUrls(HttpServletRequest request) {
for (String forbidden : this.encodedUrlBlacklist) {
if (encodedUrlContains(request, forbidden)) {
throw new RequestRejectedException("The request was rejected because the URL contained a potentially malicious String \"" + forbidden + "\" "); }}for (String forbidden : this.decodedUrlBlacklist) {
if (decodedUrlContains(request, forbidden)) {
throw new RequestRejectedException("The request was rejected because the URL contained a potentially malicious String \"" + forbidden + "\" "); }}}private static boolean encodedUrlContains(HttpServletRequest request, String value) {
if (valueContains(request.getContextPath(), value)) {
return true;
}
return valueContains(request.getRequestURI(), value);
}
private static boolean decodedUrlContains(HttpServletRequest request, String value) {
if (valueContains(request.getServletPath(), value)) {
return true;
}
if (valueContains(request.getPathInfo(), value)) {
return true;
}
return false;
}
private static boolean valueContains(String value, String contains) {
returnvalue ! =null&& value.contains(contains); }}Copy the code
The rejectedBlacklistedUrls method is used to verify urls, and the logic is simple enough that I won’t go into details.
Note: Although you can manually change these restrictions in Spring Security, Songo does not recommend any changes. Each restriction has its own reason, and each restriction you release introduces unknown Security risks. We’ll talk about the use of these restrictions again when we share security attacks on the Web. Please stay tuned.
3. Summary
Didn’t expect that? Spring Security has done so much for you! That’s the chicken soup:
Your so-called quiet years, but someone is carrying something for you.
Ok, I don’t know if you got it? If you have a harvest, remember to point under the encouragement of songge oh ~