How does SpringBoot protect against XSS attacks?

Article Directory

1. XSS cross-site scripting attack

①: Introduction to XSS Vulnerabilities

②: XSS Vulnerability Classification

③: Protection suggestions

2. SQL injection attack

①: SQL injection vulnerability introduction

②: Protection suggestions

3. How to prevent XSS attacks and sql injection in SpringBoot

①: Create Xss request filter class XssHttpServletRequestWraper

②: Add the request filtering class XssHttpServletRequestWraper to Filter and inject it into the container


1. XSS cross-site scripting attack

①: Introduction to XSS Vulnerabilities

Cross-site scripting attack (XSS) refers to an attacker inserting malicious script code into a web page. When a user browses the page, the script code embedded in the web page will be parsed and executed, thereby achieving the purpose of maliciously attacking the user. XSS attacks are aimed at user-level attacks!

②: XSS Vulnerability Classification

Stored XSS: Stored XSS, persistence, the code is stored in the server, such as in personal information or published articles, insert code, if there is no filtering or filtering is not strict, then these codes will be stored in the server, users Trigger code execution when the page is accessed. This kind of XSS is more dangerous, and it is easy to cause worms and steal cookies.

Reflective XSS: Non-persistent, need to deceive users to click on the link to trigger XSS code (there is no such page and content in the server), generally easy to appear on the search page.

DOM-type XSS: Without going through the backend, the DOM-XSS vulnerability is Document Objeet Modela vulnerability based on the Document Object Model (, DOM). DOM-XSS is triggered by passing parameters through the url to control the trigger. In fact, it is also a reflection-type XSS.

③: Protection suggestions

  • Restrict user input, form data specifies the value type, for example, age can only be int, name is a combination of letters and numbers;

  • Perform html encode processing on the data;

  • Filter or remove special html tags;

  • Tags to filter javaScript events.

2. SQL injection attack

①: SQL injection vulnerability introduction

SQL Injection (SQLi) is an injection attack that executes malicious SQL statements. It gives an attacker full control over the database server behind a web application by inserting arbitrary SQL code into database queries. Attackers can use SQL injection vulnerabilities to bypass application security measures; they can bypass authentication and authorization of web pages or web applications, and retrieve the contents of the entire SQL database; they can also use SQL injection to add, modify, and delete entries in the database. Record.

SQL injection vulnerabilities can affect any website or web application that uses a SQL database such as MySQL, Oracle, SQL Server or others. Criminals may use it to gain unauthorized access to users' sensitive data: customer information, personal data, trade secrets, intellectual property, etc. SQL injection attacks are one of the oldest, most popular, and most dangerous web application vulnerabilities.

②: Protection suggestions

Using mybatis #{}can effectively prevent sql injection.

  • When using #{}:

<select id="getBlogById" resultType="Blog" parameterType=”int”>
       select id,title,author,content
       from blog where id=#{id}
</select>

Print out the executed sql statement, and you will see that sql looks like this:

select id,title,author,content from blog where id = ?

No matter what parameters are entered, the printed sql is like this. This is because mybatis has enabled the pre-compilation function. Before the sql is executed, the above sql will be sent to the database for compilation. When executing, directly use the compiled sql and replace the placeholder "?". Because sql injection can only work on the compilation process, so it is precompiled like #{}? The way to avoid the problem of sql injection is very good.

How does mybatis achieve sql precompilation?

In fact, at the bottom of the framework, it is the class in jdbc that is working. It is a subclass of Statement that we are very familiar with. Its object contains the compiled SQL statement. This "ready" method can not only improve security, but also improve efficiency when executing a sql multiple times, because the sql has been compiled and there is no need to recompile it when it is executed again. PreparedStatement PreparedStatement

  • ${}when using

<select id="orderBlog" resultType="Blog" parameterType=”map”>
       select id,title,author,content
       from blog order by ${orderParam}
</select>

Observe carefully, the format of the inline parameter #{xxx}has changed from " " ${xxx}. If we assign " orderParamid" to the parameter " ", the sql will be printed out like this:

select id,title,author,contet from blog order by id

Obviously, this cannot prevent sql injection, and parameters will directly participate in sql compilation, so injection attacks cannot be avoided. But when it comes to dynamic table names and column names, only ${}the parameter format " " can be used. Therefore, such parameters need to be processed manually in the code to prevent injection.

3. How to prevent XSS attacks and sql injection in SpringBoot

Without further ado, let's go to the code.

For Xss attacks and Sql injection, we can use filters to get rid of some requests according to business needs

①: Create Xss request filtering class XssHttpServletRequestWraper

code show as below:

public class XssHttpServletRequestWraper extends HttpServletRequestWrapper {

    Logger log = LoggerFactory.getLogger(this.getClass());

    public XssHttpServletRequestWraper() {
        super(null);
    }

    public XssHttpServletRequestWraper(HttpServletRequest httpservletrequest) {
        super(httpservletrequest);
    }

 //过滤springmvc中的 @RequestParam 注解中的参数
    public String[] getParameterValues(String s) {

        String str[] = super.getParameterValues(s);
        if (str == null) {
            return null;
        }
        int i = str.length;
        String as1[] = new String[i];
        for (int j = 0; j < i; j++) {
            //System.out.println("getParameterValues:"+str[j]);
            as1[j] = cleanXSS(cleanSQLInject(str[j]));
        }
        log.info("XssHttpServletRequestWraper净化后的请求为:==========" + as1);
        return as1;
    }

 //过滤request.getParameter的参数
    public String getParameter(String s) {
        String s1 = super.getParameter(s);
        if (s1 == null) {
            return null;
        } else {
            String s2 = cleanXSS(cleanSQLInject(s1));
            log.info("XssHttpServletRequestWraper净化后的请求为:==========" + s2);
            return s2;
        }
    }

 //过滤请求体 json 格式的
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(inputHandlers(super.getInputStream ()).getBytes ());

        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return bais.read();
            }

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

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

            @Override
            public void setReadListener(ReadListener readListener) { }
        };
    }

    public   String inputHandlers(ServletInputStream servletInputStream){
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(servletInputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (servletInputStream != null) {
                try {
                    servletInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return  cleanXSS(sb.toString ());
    }

    public String cleanXSS(String src) {
        String temp = src;

        src = src.replaceAll("<", "<").replaceAll(">", ">");
        src = src.replaceAll("\\(", "(").replaceAll("\\)", ")");
        src = src.replaceAll("'", "'");
        src = src.replaceAll(";", ";");
        // 新增
        /**-----------------------start--------------------------*/
        src = src.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
        src = src.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41");
        src = src.replaceAll("eval\\((.*)\\)", "");
        src = src.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
        src = src.replaceAll("script", "");
        src = src.replaceAll("link", "");
        src = src.replaceAll("frame", "");
        /**-----------------------end--------------------------*/
        Pattern pattern = Pattern.compile("(eval\\((.*)\\)|script)",
                Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(src);
        src = matcher.replaceAll("");

        pattern = Pattern.compile("[\\\"\\'][\\s]*javascript:(.*)[\\\"\\']",
                Pattern.CASE_INSENSITIVE);
        matcher = pattern.matcher(src);
        src = matcher.replaceAll("\"\"");

        // 增加脚本
        src = src.replaceAll("script", "").replaceAll(";", "")
                /*.replaceAll("\"", "").replaceAll("@", "")*/
                .replaceAll("0x0d", "").replaceAll("0x0a", "");

        if (!temp.equals(src)) {
            // System.out.println("输入信息存在xss攻击!");
            // System.out.println("原始输入信息-->" + temp);
            // System.out.println("处理后信息-->" + src);

            log.error("xss攻击检查:参数含有非法攻击字符,已禁止继续访问!!");
            log.error("原始输入信息-->" + temp);

            throw new CustomerException("xss攻击检查:参数含有非法攻击字符,已禁止继续访问!!");
        }
        return src;
    }

    //输出
    public void outputMsgByOutputStream(HttpServletResponse response, String msg) throws IOException {
        ServletOutputStream outputStream = response.getOutputStream(); //获取输出流
        response.setHeader("content-type", "text/html;charset=UTF-8"); //通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码
        byte[] dataByteArr = msg.getBytes("UTF-8");// 将字符转换成字节数组,指定以UTF-8编码进行转换
        outputStream.write(dataByteArr);// 使用OutputStream流向客户端输出字节数组
    }

    // 需要增加通配,过滤大小写组合
    public String cleanSQLInject(String src) {
        String lowSrc = src.toLowerCase();
        String temp = src;
        String lowSrcAfter = lowSrc.replaceAll("insert", "forbidI")
                .replaceAll("select", "forbidS")
                .replaceAll("update", "forbidU")
                .replaceAll("delete", "forbidD").replaceAll("and", "forbidA")
                .replaceAll("or", "forbidO");

        if (!lowSrcAfter.equals(lowSrc)) {
            log.error("sql注入检查:输入信息存在SQL攻击!");
            log.error("原始输入信息-->" + temp);
            log.error("处理后信息-->" + lowSrc);
            throw new CustomerException("sql注入检查:参数含有非法攻击字符,已禁止继续访问!!");
        }
        return src;
    }
}

②: Add the request filtering class  XssHttpServletRequestWraper to Filter and inject it into the container

@Component
public class XssFilter implements Filter {

    Logger log  = LoggerFactory.getLogger(this.getClass());

    // 忽略权限检查的url地址
    private final String[] excludeUrls = new String[]{
            "null"
    };

    public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
            throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) arg0;
        HttpServletResponse response = (HttpServletResponse) arg1;

        String pathInfo = req.getPathInfo() == null ? "" : req.getPathInfo();
        //获取请求url的后两层
        String url = req.getServletPath() + pathInfo;
        //获取请求你ip后的全部路径
        String uri = req.getRequestURI();
        //注入xss过滤器实例
        XssHttpServletRequestWraper reqW = new XssHttpServletRequestWraper(req);

        //过滤掉不需要的Xss校验的地址
        for (String str : excludeUrls) {
            if (uri.indexOf(str) >= 0) {
                arg2.doFilter(arg0, response);
                return;
            }
        }
        //过滤
        arg2.doFilter(reqW, response);
    }
    public void destroy() {
    }
    public void init(FilterConfig filterconfig1) throws ServletException {
    }
}

The above code can already complete the filtering of request parameters and JSON request body, but there are other ways to realize the JSON request body. If you are interested, please see the extension below!

For more content, please pay attention to the official account [Programmer Style] to get more exciting content!

Extension: You can also rewrite the Json request body in spring  MappingJackson2HttpMessageConverter to filter

Because the request body will go through a transformation when it enters and exits Contoroller  MappingJackson2HttpMessageConverter to convert the request body into the json format we need, so you can make some modifications here!

@Configuration
public class MyConfiguration {

    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){
        //自定义转换器
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();

        //转换器日期格式设置
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleDateFormat smt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        objectMapper.setDateFormat(smt);
        converter.setObjectMapper(objectMapper);

        //转换器添加自定义Module扩展,主要是在这里做XSS过滤的!!,其他的是其他业务,不用看
        SimpleModule simpleModule = new SimpleModule();
        //添加过滤逻辑类!
        simpleModule.addDeserializer(String.class,new StringDeserializer());
        converter.getObjectMapper().registerModule(simpleModule);

        //设置中文编码格式
        List<MediaType> list = new ArrayList<>();
        list.add(MediaType.APPLICATION_JSON_UTF8);
        converter.setSupportedMediaTypes(list);

        return converter;
    }

}

The real filtering logic class : StringDeserializer

//检验请求体的参数
@Component
public class StringDeserializer extends JsonDeserializer<String> {
    @Override
    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        String str = jsonParser.getText().trim();
        //sql注入拦截
        if (sqlInject(str)) {
          throw new CustomerException("参数含有非法攻击字符,已禁止继续访问!");
        }

        return xssClean(str);

    }

    public boolean sqlInject(String str) {

        if (StringUtils.isEmpty(str)) {
            return false;
        }

        //去掉'|"|;|\字符
        str = org.apache.commons.lang3.StringUtils.replace(str, "'", "");
        str = org.apache.commons.lang3.StringUtils.replace(str, "\"", "");
        str = org.apache.commons.lang3.StringUtils.replace(str, ";", "");
        str = org.apache.commons.lang3.StringUtils.replace(str, "\\", "");

        //转换成小写
        str = str.toLowerCase();

        //非法字符
        String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alert","alter", "drop"};

        //判断是否包含非法字符
        for (String keyword : keywords) {
            if (str.indexOf(keyword) != -1) {
                return true;
            }
        }
        return false;

    }

    //xss攻击拦截

    public String xssClean(String value) {
        if (value == null || "".equals(value)) {
            return value;
        }

        //非法字符
        String[] keywords = {"<", ">", "<>", "()", ")", "(", "javascript:", "script","alter", "''","'"};
        //判断是否包含非法字符
        for (String keyword : keywords) {
            if (value.indexOf(keyword) != -1) {
               throw new CustomerException("参数含有非法攻击字符,已禁止继续访问!");
            }
        }

        return value;
    }
}

Using this form can also complete the filtering of the json request body, but personally recommend using  XssHttpServletRequestWraper the form to complete the xss filtering! !

For more content, please pay attention to the official account [Programmer Style] to get more exciting content! 

Guess you like

Origin blog.csdn.net/dreaming317/article/details/129787078
Recommended