如何阻止人们在Spring MVC中进行XSS操作?

时间:2010-01-27 15:10:56

标签: spring jsp spring-mvc xss html-escape-characters

我应该怎么做才能阻止Spring MVC中的XSS?现在我只是将输出用户文本的所有地方放到JSTL <c:out>标签或fn:escapeXml()函数中,但这似乎很容易出错,因为我可能会错过一个地方。

有一种简单的系统方法可以防止这种情况发生吗?也许像过滤器或什么?我通过在控制器方法上指定@RequestParam参数来收集输入。

8 个答案:

答案 0 :(得分:53)

在Spring中,您可以从<form>标记生成的JSP页面中转义html。这为XSS攻击提供了很多途径,可以通过三种方式自动完成:

对于web.xml文件中的整个应用程序:

<context-param>
    <param-name>defaultHtmlEscape</param-name>
    <param-value>true</param-value>
</context-param>

对于文件本身中给定页面上的所有表单:

<spring:htmlEscape defaultHtmlEscape="true" /> 

每种表格:

<form:input path="someFormField" htmlEscape="true" /> 

答案 1 :(得分:8)

尝试XSSFilter

答案 2 :(得分:7)

我通过@Valid使用Hibernate Validator获取所有输入对象(绑定和@RequestBody json,请参阅https://dzone.com/articles/spring-31-valid-requestbody)。所以@org.hibernate.validator.constraints.SafeHtml对我来说是一个很好的解决方案。

Hibernate SafeHtmlValidator取决于org.jsoup,因此需要再添加一个项目依赖项:

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.10.1</version>
</dependency>

对于带有字段

的bean User
@NotEmpty
@SafeHtml
protected String name;

在控制器中使用值<script>alert(123)</script>进行更新尝试

@PutMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE)
public void update(@Valid @RequestBody User user, @PathVariable("id") int id) 

@PostMapping
public void createOrUpdate(@Valid User user) {

用于绑定BindExceptionMethodArgumentNotValidException@RequestBody抛出默认消息:

name may have unsafe html content

Validator也适用于绑定,就像之前的持久化一样。 可以在http://topjava.herokuapp.com/

上测试应用

答案 3 :(得分:6)

当您尝试阻止XSS时,考虑上下文非常重要。例如,如果您在javascript片段中输出变量中的数据而不是在HTML标记或HTML属性中输出数据,那么如何以及要逃避的内容就会大不相同。

我在这里有一个例子:http://erlend.oftedal.no/blog/?blogid=91

同时查看OWASP XSS预防备忘单:http://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet

所以简短的回答是,确保你像Tendayi Mawushe建议的那样逃避输出,但在输出HTML属性或javascript中的数据时要特别小心。

答案 4 :(得分:0)

您是如何首先收集用户输入的?如果您使用FormController

,此问题/答案可能会有所帮助

Spring: escaping input when binding to command

答案 5 :(得分:0)

始终手动检查您使用的方法,标签,并确保它们总是在最后逃逸(一次)。框架在这方面存在许多缺陷和差异。

概述:http://www.gablog.eu/online/node/91

答案 6 :(得分:0)

不应仅依赖于<c:out />,还应使用反编译库,它不仅可以编码,还可以清理输入中的恶意脚本。可用的最好的库之一是OWASP Antisamy,它非常灵活,可以根据需要配置(使用xml策略文件)。

例如如果应用程序仅支持文本输入,则可以使用OWASP提供的大多数通用policy file来清理和删除大多数html标记。类似地,如果应用程序支持需要所有类型的html标记的html编辑器(例如tinymce),则可以使用更灵活的策略,例如ebay policy file

答案 7 :(得分:0)

library(dplyr)
library(ggplot2)
# use mtcars data set
data <- mtcars

# aggregate data by grouping variables
aggregate_data<- data%>%
  group_by(gear,cyl, carb)%>%
  summarize(mpg=mean(mpg))%>%
  ungroup

# get total for gear
data_tot_cyl<- data%>%
  group_by(cyl, carb)%>%
  summarize(mpg=mean(mpg))%>%
  ungroup%>%
  mutate(gear='total')

# get total for cyl
data_tot_gear<- data%>%
  group_by(gear, carb)%>%
  summarize(mpg=mean(mpg))%>%
  ungroup%>%
  mutate(cyl='total')

# get total for total-total
data_tot_tot<- data%>%
  group_by(carb)%>%
  summarize(mpg=mean(mpg))%>%
  ungroup%>%
  mutate(cyl='total', gear='total')

# get data frame with all total's data.
new_data<-data_tot_tot%>%
  bind_rows(data_tot_gear%>%mutate(gear=as.character(gear)))%>%
  bind_rows(data_tot_cyl%>%mutate(cyl=as.character(cyl)))%>%
  bind_rows(aggregate_data%>%mutate_at(vars(gear, cyl), funs(as.character)))

# Arghh, gotta order the levels so total is at the end.
new_data$cyl <- factor(new_data$cyl, 
                         levels=c('4','6','8','total'),ordered=T)
new_data$gear <- factor(new_data$gear, 
                        levels=c('3','4','5','total'),ordered=T)

# Finally after over 20 additional lines of code, I get the 
# faceted plot with totals for x and y facets. 
p<-ggplot(new_data, aes(x=carb, y=mpg))+
  geom_bar(stat='identity')+
  facet_grid(cyl~gear)+
  geom_text(aes(label=round(mpg,1), y=0), 
            col='white', size=3,hjust=-0.3, angle=90)+
  ggtitle('Average MPG vs Num Carbs, by Num Cylinders & Num Gears')
print(p)
  

XSS问题的解决方案是过滤表单中的所有文本字段   在提交表格时。

**To avoid XSS security threat in spring application**

名为RequestWrapper.java的代码第二类是:

package com.filter;

    It needs XML entry in the web.xml file & two simple classes.

        java code :-
        The code for the  first class named CrossScriptingFilter.java is :

        package com.filter;

        import java.io.IOException;
        import javax.servlet.Filter;
        import javax.servlet.FilterChain;
        import javax.servlet.FilterConfig;
        import javax.servlet.ServletException;
        import javax.servlet.ServletRequest;
        import javax.servlet.ServletResponse;
        import javax.servlet.http.HttpServletRequest;
        import org.apache.log4j.Logger;

        public class CrossScriptingFilter implements Filter {
            private static Logger logger = Logger.getLogger(CrossScriptingFilter.class);
            private FilterConfig filterConfig;

            public void init(FilterConfig filterConfig) throws ServletException {
                this.filterConfig = filterConfig;
            }

            public void destroy() {
                this.filterConfig = null;
            }

            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
                logger.info("Inlter CrossScriptingFilter  ...............");
                chain.doFilter(new RequestWrapper((HttpServletRequest) request), response);
                logger.info("Outlter CrossScriptingFilter ...............");
            }

        }
  

唯一剩下的就是web.xml文件中的XML条目:

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.log4j.Logger;

public final class RequestWrapper extends HttpServletRequestWrapper {
    private static Logger logger = Logger.getLogger(RequestWrapper.class);
    public RequestWrapper(HttpServletRequest servletRequest) {
        super(servletRequest);
    }

    public String[] getParameterValues(String parameter) {
        logger.info("InarameterValues .. parameter .......");
        String[] values = super.getParameterValues(parameter);
        if (values == null) {
            return null;
        }
        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = cleanXSS(values[i]);
        }
        return encodedValues;
    }

    public String getParameter(String parameter) {
        logger.info("Inarameter .. parameter .......");
        String value = super.getParameter(parameter);
        if (value == null) {
            return null;
        }
        logger.info("Inarameter RequestWrapper ........ value .......");
        return cleanXSS(value);
    }

    public String getHeader(String name) {
        logger.info("Ineader .. parameter .......");
        String value = super.getHeader(name);
        if (value == null)
            return null;
        logger.info("Ineader RequestWrapper ........... value ....");
        return cleanXSS(value);
    }

    private String cleanXSS(String value) {
        // You'll need to remove the spaces from the html entities below
        logger.info("InnXSS RequestWrapper ..............." + value);
        //value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
        //value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
        //value = value.replaceAll("'", "& #39;");
        value = value.replaceAll("eval\\((.*)\\)", "");
        value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");

        value = value.replaceAll("(?i)<script.*?>.*?<script.*?>", "");
        value = value.replaceAll("(?i)<script.*?>.*?</script.*?>", "");
        value = value.replaceAll("(?i)<.*?javascript:.*?>.*?</.*?>", "");
        value = value.replaceAll("(?i)<.*?\\s+on.*?>.*?</.*?>", "");
        //value = value.replaceAll("<script>", "");
        //value = value.replaceAll("</script>", "");
        logger.info("OutnXSS RequestWrapper ........ value ......." + value);
        return value;
    }

/ *表示对于浏览器发出的每个请求,它都会调用     CrossScriptingFilter类。这将解析所有组件/元素来自请求&amp;     将用空字符串替换黑客提出的所有javascript标签,即