System protection becomes a paper tiger

Note that I have omitted extraneous code for all of the code examples below, leaving only the critical code in place. If you’re interested in more details, check out the source code.

I like to say some digressions about programmers at the end of the article, to help programmers grow, you can have a look.

The story begins two days ago when the company discovered a loophole.

We all know that you need to log in when browsing websites. Only after you have obtained the appropriate authorization can you proceed further. Like http://xxx/page/administrator this interface, for example, in the case of not login, access will return the results:

The most recently discovered vulnerability is for services deployed on Jetty. By adding the backslash can bypass the loophole, namely through http://xxx//page/administrator can return to normal data (for security reasons, here don’t show the normal return data), don’t need to!!!!!!

Why is authentication bypassed

Jetty uses a strict matching mode when matching filter. For the /page/administrat path, it matches the /page/* rule and is intercepted by an authentication filter (such as SSOFilter) for authentication.

However, the //page/administrat path with two backslashes does not fall under the above hit rule and therefore will not be blocked, thus driving straight in. This is scary!!

The solution

Methods a

Directly set the matching rule of the authentication filter to /* to block all access. So there’s no way around it, it’s so simple, it’s a no-brainer.

There’s a little bit of a problem with this, because we know that services in big companies usually have health checks. The monitoring system accesses the health check path and returns HTTP return code 200 OK to verify that the service is normal. The monitoring system does not carry authentication information when accessing the health check path.

Therefore, you have to exclude the health check path separately from the interceptor, which is not elegant. If you do not exclude the health check path, you will always be unable to return 200 OK because of authentication failure, causing the monitoring system to think that your service has not been running, and then issue an alarm. So there’s method two.

Method 2

In normal access, there will be no illegal requests such as //XXX/YY with consecutive backslashes. So, I add another interceptor in front of the authentication interceptor, which will filter the illegal request directly.

public void doFilter(ServletRequest request,ServletResponse response, FilterChain filterChain)
{
  String  url= request.getRequestURL();
  if(! url.indexOf("/ /") <0)
  { // Obviously, there is no such thing in the company code, but the meaning is similar
    response.getWriter().write("Stop messing with Grandpa and type in the address.")}else{... }}Copy the code

Response.getwriter ().write(” Don’t mess with grandpa, just type in the address “) is the essence.

** you crazy as you crazy, I directly board brick broken martial arts! The logic is simple, but effective. If you don’t have the right format, you won’t get access at all. If you have any tricks, come around and I’ll count you as winner!

However, you might ask, url. IndexOf (” / / “) < 0 can only judge / /, that / / / or / / / / and so on this? Friend, /// there is a //? It will be detected!

I don’t want my pants down

We did solve the problem up there, and the safety hazard has been solved for the time being. But have you ever thought about a problem?

When hacker little brother’s browser sees this sentence don’t mess with grandpa, obedient input address, our SSO is in effect? Or, after all, his access request was intercepted by our format check filter after bypassing SSOFilter. Or is it blocked by format check filter first, not even touch SSOFilter at all?

There is a qualitative difference. The first is he pulls your pants down, but you put them back up at the speed of light. The latter is when you tighten your belt so that he can’t take it off and tease him with your mocking eyes. Both although there is no risk of going naked, but obviously the latter sense of security.

So, is our system the former or the latter? ** Depends on the order in which filters are loaded in the system! ** If you want to talk about this problem, it belongs to the children have no mother, it’s a long story. Let’s talk about that today.

Filter’s past lives

role

A filter on the Web is commonly referred to as a filter, or interceptor. You can almost tell by the name what it does.

In fact, a Filter is similar to a servlet and can be considered another special form of servlet. Like servlets, requests are processed, except that the filter does not generate a response back to the user.

Filter plays an important role in Web applications, and many functions need to be completed by Filter. For example, common authentication, access logs, encoding and decoding, and interception of special requests can be implemented using filter.

Configuration and Examples

There are two ways to configure a Filter in a Web application. One is through web. XML, the other is through annotations.

Here we define a Filter that implements a simple function: when there is an access, our console outputs the statement “someone visited”. Use this example to show the two configurations separately.

Define a Filter

package quLunBianCheng;
public class DemoFilter implements Filter {

    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println(When the container starts, it calls init to instantiate DemoFilter.);
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("We have a visitor.");
        chain.doFilter(request, response);
    }

    public void destroy(a) {
        System.out.println("When DemoFilter is to be destroyed, the destroy method is called."); }}Copy the code

The XML configuration

<filter>
   <filter-name>DemoFilter</filter-name>
   <filter-class>quLunBianCheng.DemoFilter</filter-class>
 </filter>
 <filter-mapping>
    <filter-name>DemoFilter</filter-name>
    <url-pattern>/ *</url-pattern>
 </filter-mapping>
Copy the code

This configuration should be familiar; you can always see it in web.xml.

/*
means that all access will be blocked by DemoFilter. Therefore, every time there is an access, your console will say someone is visiting

From there, you can derive more complex features. For example, you can count the number of times your web page is visited by slightly rewriting DemoFilter’s doFilter method.

public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("We have a visitor.");
      	// Number of visits +1Constant. VisitCount++; System.out.println("Your web page has been"+Constant.visitCount+"People have visited.");  
        chain.doFilter(request, response);
    }
 
Copy the code

@ WebFilter annotations

The annotations form has many benefits, such as a more comfortable look, cleaner code, and no XML configuration files. However, it is essentially the same as XML, just in a different way.

There is no need to configure the XML file in the annotation mode, just make a small change to DemoFilter.

package quLunBianCheng;
@WebFilter(filterName = "DemoFilter",urlPatterns = "/*")
public class DemoFilter implements Filter {

    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println(When the container starts, it calls init to instantiate DemoFilter.);
    }
		// For brevity, I have omitted the dofilter and destory methods.
}
Copy the code

This is done by adding @webFilter (filterName = “DemoFilter”,urlPatterns = “/*”).

In fact, there are many more optional configuration parameters for the @webFilter annotation that are listed at the end of this article – > the @webFilter attribute appendix

Filters are important, but don’t be afraid. What? Do you want to know the initialization routine of Filter? This is for the next article. I won’t be able to write this article. Because there are a lot of things to talk about below, not enough space.

The birth of an authentication Filter

This article started with an authentication problem, so let’s move on to authentication. The previous Filter is used to introduce authentication Filter.

Login authentication filter

In fact, the authentication filter itself is a filter, there is no mystery. An authentication filter is implemented every minute.

Change the DemoFilter above to become a login authentication browser.

package quLunBianCheng;
public class LoginFilter implements Filter {
    // omit non-critical code
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // Get the request
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        / / get a cookie
        Cookie[] cookies = request.getCookies();
      	// Iterate over the cookie to find the username and password
      	String username = "";
        String password = "";
      	if(cookies ! =null && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals("username")) {
                    username = cookie.getValue();
                } else if (cookie.getName().equals("password")) { password = cookie.getValue(); }}}// If there is no cookie in the first login, or the user name and password are inconsistent, let the user log in again
      	if (username.equals("") || password.equals("") | | (! username.equals("XXX") | | (! password.equals("XXXXXXX")) {
          	// Redirect him to login
          	response.sendRedirect("login.jsp");	  
          	return ;
        } else{
          // If it passes, it can be releasedchain.doFilter(request,response); }}}Copy the code

In the above code! Username. Equals (” XXX “) | | (! Password.equals (“XXXXXXX”) is just a simple demonstration, but in real projects this is actually queried from the database, not XXXXXX

You can use the web. XML or @webfilter annotation to configure the Filter. Please don’t use both methods…)

As you can see, implementing a simple login filter is very simple. But in a real production environment, you don’t do that. Why? Because it’s not safe! (** Programmer brother for your data security is really painstaking, so promise me not to set the password to AAA123 or 123456789, ok? Online kneel to kowtow!!!! * *).

All right, let’s see why that’s not safe. Because we directly store the user name and password in the cookie, the cookie is stored in the user’s browser, which is easy to crack. It is difficult for users to have the professional ability to protect cookie information. Cookies that contain a lot of personally important information (not just passwords, but potentially information about yourself in the physical world) are dangerous behavior.

How to do? Let’s say we put a meaningless id in the cookie, and every time you request this id, the server uses this id to find information about the user (called a session) that exists on the server. This id is the sessionId. Even if the hacker gets the cookie, he cannot directly solve your personal information from the cookie, so it is relatively safer (after all, the ability of the server to resist attack is much stronger than that of the user side, if properly protected, it is much more difficult for the hacker to obtain sensitive information from the server).

Therefore, the LoginFilter can be changed to the SSOFilter below.

ssoFilter

package quLunBianCheng;
public class SsoFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {
      /* The initialization method receives a parameter of type FilterConfig which is some configuration of Filter */
    }
    public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
       / * this will not repeat code, with pseudo code says * /
        1: As above, filter ssoId from Cookies2: Then take this ssoId to the server to query the corresponding login name and password3: If the login name and password match, the system permits the login. If not, log in again}}Copy the code

In this way, we start from a vulnerability, a complete description of Filter-> authentication Filter->SSOFilter. Detailed examples are provided for each step, even including configuration information.

But will that be enough? NO, it’s still not Ok. I’m here!! In an article, I said that technical people must learn to ask questions.

In a further step

In this section, we’ll start with different declarations, throw out more classes about filters and try to explain how they work in detail.

If not specified. The SsoFilter mentioned in this section is the SsoFilter mentioned in the previous section

Have you ever seen a configuration like this in web.xml?

<filter>
		<filter-name>SsoFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
<filter-mapping>
    <filter-name>SsoFilter</filter-name>
    <url-pattern>/ *</url-pattern>
</filter-mapping>
Copy the code

Or this configuration?

@Bean
public FilterRegistrationBean<Filter> LoginFilter(a) {
        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new SsoFilter());
        registration.addUrlPatterns("/ *");
        registration.setName("SsoFilter");
        registration.setOrder(0);
        registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.FORWARD);
        return registration;
    }
Copy the code

Or this configuration?

@Bean
public DelegatingFilterProxyRegistrationBean ssoFilterProxyRegistrationBean(a) {
    DelegatingFilterProxyRegistrationBean filterRegistration = new DelegatingFilterProxyRegistrationBean("ssoFilter");
    filterRegistration.addInitParameter("targetFilterLifecycle"."true");
    filterRegistration.addUrlPatterns("/ *");
    filterRegistration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.FORWARD);
    filterRegistration.setOrder(2);
    return filterRegistration;
}
Copy the code

Chances are you’ve seen it, but you haven’t noticed or paid attention to why. In fact, these three methods achieve the same function as the previous ssoFilter, but with the use of proxies.

I’m fine. Why do I have to represent for a while? Because, ** through proxy, we can make our own defined ssoFilter and container perfect integration, enjoy the function of Spring container, use Spring to manage the life cycle of Filter, Filter if some beans can be directly obtained through injection method. ** In addition, there are many advantages. Therefore, it is important to understand these proxy methods.

We see this in three ways, the appearance of three new proxy class DelegatingFilterProxy, FilterRegistrationBean and DelegatingFilterProxyRegistrationBean.

I wanted to cover the rest in this article, but I felt it was too long, so I decided to divide it into two phases.

In our next post, we’ll explain how these three agents work and answer the pants-pulling question.

The appendix

Health check

The so-called health check means that the controller has an access interface and the general path is XXX /alive. If the HTTP response code returned is 200, then the task service is already running. If you can’t return 200, then the system must now have a problem.

@webFilter attribute appendix

The property name type describe
filterName String Specify the name attribute of the filter
value String[] This property is equivalent to the urlPatterns property. But they should not be used together
urlPatterns String[] Specifies the URL matching pattern for a set of filters. Equivalent to tag
servletNames String[] Specifies which servlets the filter will be applied to. The value is the value of the name attribute in @webservlet, or the value in web.xml
dispatcherTypers DispatcherType The value can be ASYNC, ERROR, FORWARD, INCLUDE, or REQUEST.
initParams WebInitParam[] Specifies a set of filter initialization parameters, equivalent to labels
asyncSupported boolean Declare whether the filter supports asynchronous mode of operation, equivalent to labels
description String Description of the filter is equivalent to a label
displayName String The display name of the filter, usually used with tools, is equivalent to the label

In fact, any of the annotations of the parameters, we can not search through Baidu. For a technician, acquiring firsthand information is a very important skill.

You command the annotation and you jump to the definition of the annotation. @webFilter is defined as follows:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebFilter {

    String description(a) default "";
    
    String displayName(a) default "";
    
    WebInitParam[] initParams() default {};
    
    String filterName(a) default "";
    
    String smallIcon(a) default "";

    String largeIcon(a) default "";

    String[] servletNames() default {};
    
    String[] value() default {};

    String[] urlPatterns() default {};

    DispatcherType[] dispatcherTypes() default {DispatcherType.REQUEST};
    
    boolean asyncSupported(a) default false;
}
Copy the code

You can clearly see which parameters are available, and each one is clearly and unambiguously commented. Do it yourself!

quotations

Quote:

If a man cannot swim, he will not swim in any other pool.

Resolution:

See, some programmers always complain that they don’t have a good opportunity, that they maintain some shit code, no challenge, only redundant and meaningless things. In fact, nothing is meaningless. If you can look at a problem from a different Angle, with a different attitude to some situations, you will not have this complaint.

Give anyone a good chance and the results won’t be too bad. But those who truly surpass others do not stand out by fate. Those who complain that opportunities are not available probably want to work too hard and expect to get rich overnight. The orderly migrant workers who build bricks or stones on the wall can always get the appreciation of the contractor. Do any one thing to the extreme, is not a meaningless thing. What’s more, the situation and starting point of the people who read my article are countless times better than the people at the bottom of the manual labor.