Springboot defends against cross-site scripting (XSS) attacks

1. The harm of XSS attacks  

       XSS attacks usually refer to exploiting vulnerabilities left during web development and injecting malicious instruction code into the network through clever methods.
page, allowing users to load and execute web programs maliciously created by attackers. These malicious web programs are usually JavaScript , but in reality
It can actually include Java , VBScript , ActiveX , Flash or even plain HTML . After the attack is successful, the attack
The user may obtain various permissions including but not limited to higher permissions (such as performing some operations), private web content, sessions and cookies, etc.
content.
         For example, when a user posts or registers, he or she enters <script>alert('xss')</script> in the text box . This code
If it is not escaped, it will be saved directly to the database. When the view layer renders HTML in the future , output this code to
page, then the content of the <script> tag will be executed.
        Normally, we log into a website. If the website uses HttpSession to save login credentials, then
SessionId will be saved on the browser in the form of a cookie . If a hacker posts on this page, fill in the
The JavaScript code is used to obtain the cookie content and send the cookie content to the hacker's own email via Ajax .
brain. So as long as someone browses the hacker's post on this website, the view layer will render the HTML page and the injection will be performed.
XSS script, so your cookie information is leaked. Hackers create cookies on their computers and can impersonate
Recharge logged in users.
   Even though many websites use JWT , the login credentials ( Token token ) are stored in the browser. So using XSS script can
By easily extracting the Token from Storage , hackers can still easily impersonate logged-in users.
        
Therefore , the most effective way     to avoid XSS attacks is to escape the data entered by the user and then store it in the database. wait until
When the view layer renders the HTML page. Escaped text will not be executed as JavaScript , which can resist XSS
attack.

2. Import dependent libraries

        Because the Hutool tool kit comes with XSS escape tool classes, we need to import Hutool and then use the Servlet specification

Provided request wrapper class that defines data escaping functionality.
 
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.4.0</version>
</dependency>

3. Define the request packaging class

The HttpServletRequest       we usually encounter when writing Web projects is actually an interface. If we want to redefine please
When looking for a class, extending this interface is the last thing you should do. Because there are too many abstract methods in the HttpServletRequest interface, we will
It's too time consuming to implement. So we should choose a simpler way to customize the request class. That is inheritance
HttpServletRequestWrapper parent class.
        
      JavaEE is just a standard, and the specific implementation is completed by various application server manufacturers. For example, Tomcat is implementing
When the Servlet was standardized, the implementation class of the HttpServletRequest interface was customized . At the same time, the JavaEE specification also defines
HttpServletRequestWrapper , this class is a wrapper class of the request class, using the decorator pattern. I have to say here
The design patterns used are really great. No matter how each application server manufacturer implements the HttpServletRequest interface,
If the user wants to customize the request, he only needs to inherit HttpServletRequestWrapper and override a certain method accordingly, and then
Pass the request into the request wrapper class, and the decorator pattern will replace a corresponding method in the request object. User code and server
The manufacturer's code is completely decoupled. We don't need to care about how the HttpServletRequest interface is implemented. With the help of the packaging class, we
We are free to modify the method in the request. Students, with such an elegant code design, you should really study the design model seriously when you have time.
Mode.
package com.example.emos.wx.config.xss;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import cn.hutool.json.JSONUtil;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.Map;

public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
    public XssHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    @Override
    public String getParameter(String name) {
        String value= super.getParameter(name);
        if(!StrUtil.hasEmpty(value)){
            value=HtmlUtil.filter(value);
        }
        return value;
    }

    @Override
    public String[] getParameterValues(String name) {
        String[] values= super.getParameterValues(name);
        if(values!=null){
            for (int i=0;i<values.length;i++){
                String value=values[i];
                if(!StrUtil.hasEmpty(value)){
                    value=HtmlUtil.filter(value);
                }
                values[i]=value;
            }
        }
        return values;
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> parameters = super.getParameterMap();
        LinkedHashMap<String, String[]> map=new LinkedHashMap();
        if(parameters!=null){
            for (String key:parameters.keySet()){
                String[] values=parameters.get(key);
                for (int i = 0; i < values.length; i++) {
                    String value = values[i];
                    if (!StrUtil.hasEmpty(value)) {
                        value = HtmlUtil.filter(value);
                    }
                    values[i] = value;
                }
                map.put(key,values);
            }
        }
        return map;
    }

    @Override
    public String getHeader(String name) {
        String value= super.getHeader(name);
        if (!StrUtil.hasEmpty(value)) {
            value = HtmlUtil.filter(value);
        }
        return value;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        InputStream in= super.getInputStream();
        InputStreamReader reader=new InputStreamReader(in, Charset.forName("UTF-8"));
        BufferedReader buffer=new BufferedReader(reader);
        StringBuffer body=new StringBuffer();
        String line=buffer.readLine();
        while(line!=null){
            body.append(line);
            line=buffer.readLine();
        }
        buffer.close();
        reader.close();
        in.close();
        Map<String,Object> map=JSONUtil.parseObj(body.toString());
        Map<String,Object> result=new LinkedHashMap<>();
        for(String key:map.keySet()){
            Object val=map.get(key);
            if(val instanceof String){
                if(!StrUtil.hasEmpty(val.toString())){
                    result.put(key,HtmlUtil.filter(val.toString()));
                }
            }
            else {
                result.put(key,val);
            }
        }
        String json=JSONUtil.toJsonStr(result);
        ByteArrayInputStream bain=new ByteArrayInputStream(json.getBytes());
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bain.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }
        };
    }
}

4. Create a filter and pass all request objects into the packaging class

        In order to make the wrapper class just defined take effect, we also need to create it in com.example.emos.wx.config.xss

XssFilter filter. The filter intercepts all requests and then passes the requests to the wrapper class so that the wrapper class can cover all requests.
The parameter method of the request, the user obtains the data from the request, all of which are escaped.
package com.example.emos.wx.config.xss;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class XssFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request= (HttpServletRequest) servletRequest;
        XssHttpServletRequestWrapper wrapper=new XssHttpServletRequestWrapper(request);
        filterChain.doFilter(wrapper,servletResponse);
    }

    @Override
    public void destroy() {

    }
}

5. Add annotations to the main class

        Add the @ServletComponentScan annotation to the SpringBoot main class .

Guess you like

Origin blog.csdn.net/qq_67801847/article/details/133181410