如何防止人们在 Spring MVC 中进行 XSS?

发布于 2024-08-19 16:53:07 字数 231 浏览 6 评论 0原文

Spring MVC 中如何防止 XSS?现在,我只是将输出用户文本的所有位置放入 JSTL 标记或 fn:escapeXml() 函数中,但这似乎很容易出错,因为我可能会错过一个地方。

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

What should I do to prevent XSS in Spring MVC? Right now I am just putting all places where I output user text into JSTL <c:out> tags or fn:escapeXml() functions, but this seems error prone as I might miss a place.

Is there an easy systematic way to prevent this? Maybe like a filter or something? I'm collecting input by specifying @RequestParam parameters on my controller methods.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

小姐丶请自重 2024-08-26 16:53:08

不应仅依赖 ,还应该使用 antixss 库,它不仅可以编码,还可以清理输入中的恶意脚本。 OWASP Antisamy 是最好的库之一,它非常灵活并且可以配置(使用 xml 策略文件)根据要求。

例如,如果应用程序仅支持文本输入,则最通用的 策略文件 来清理和删除大部分 html 标签。同样,如果应用程序支持需要各种 html 标签的 html 编辑器(例如 tinymce),则可以使用更灵活的策略,例如 eBay 政策文件

Instead of relying only on <c:out />, an antixss library should also be used, which will not only encode but also sanitize malicious script in input. One of the best library available is OWASP Antisamy, it's highly flexible and can be configured(using xml policy files) as per requirement.

For e.g. if an application supports only text input then most generic policy file provided by OWASP can be used which sanitizes and removes most of the html tags. Similarly if application support html editors(such as tinymce) which need all kind of html tags, a more flexible policy can be use such as ebay policy file

蘑菇王子 2024-08-26 16:53:08
**To avoid XSS security threat in spring application**

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

    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 ...............");
            }

        }

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

package com.filter;

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;
    }

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

        <filter>
        <filter-name>XSS</filter-name>
        <display-name>XSS</display-name>
        <description></description>
        <filter-class>com.filter.CrossScriptingFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>XSS</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

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

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

solution to the XSS issue is to filter all the textfields in the form
at the time of submitting the form.

    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 ...............");
            }

        }

The code second class named RequestWrapper.java is :

package com.filter;

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;
    }

The only thing remained is the XML entry in the web.xml file:

        <filter>
        <filter-name>XSS</filter-name>
        <display-name>XSS</display-name>
        <description></description>
        <filter-class>com.filter.CrossScriptingFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>XSS</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

The /* indicates that for every request made from browser, it will call
CrossScriptingFilter class. Which will parse all the components/elements came from the request &
will replace all the javascript tags put by hacker with empty string i.e

白龙吟 2024-08-26 16:53:07

在 Spring 中,您可以转义

标记生成的 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" /> 

In Spring you can escape the html from JSP pages generated by <form> tags. This closes off a lot avenues for XSS attacks, and can be done automatically in three ways:

For the entire application in the web.xml file:

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

For all forms on a given page in the file itself:

<spring:htmlEscape defaultHtmlEscape="true" /> 

For each form:

<form:input path="someFormField" htmlEscape="true" /> 
打小就很酷 2024-08-26 16:53:07

我通过 @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>

For bean User with field

@NotEmpty
@SafeHtml
protected String name;

for update attempts with value < code>在控制器中

@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) {

对于绑定抛出 BindException ,对于 @RequestBody< 抛出 MethodArgumentNotValidException /code> 带有默认消息:

name may have unsafe html content

验证器也适用于绑定,就像之前的持久化一样。
应用程序可以在 http://topjava.herokuapp.com/ 进行测试

更新:另请参阅@GuyT 的评论

CVE-2019-10219 和 @SafeHtml 的状态

我们已获悉与 @SafeHtml 约束相关的 CVE-2019-10219,该问题已在 6.0.18.Final 和 6.1.0.Final 中修复......

但是,我们得出的结论是,@SafeHtml 约束很脆弱,对安全性高度敏感,并且依赖于并非为此目的而设计的外部库。将其包含在核心 Hibernate Validator 中并不是一个好主意。这就是我们弃用它并将其标记为删除的原因。这里没有神奇的计划,因此我们的用户必须自己维护此约束

我自己的简历:它是安全的并且可以使用,直到找到更好的解决方案。

更新:由于从 hibernate.validator 中删除 @SafeHtml/SafeHtmlValidator 使用自己的 NoHtmlValidator,请参阅 https://stackoverflow.com/a/68888601/548473

I use Hibernate Validator via @Valid for all input objects (binding and @RequestBody json, see https://dzone.com/articles/spring-31-valid-requestbody). So @org.hibernate.validator.constraints.SafeHtml is a good solution for me.

Hibernate SafeHtmlValidator depends on org.jsoup, so it's needed to add one more project dependencies:

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

For bean User with field

@NotEmpty
@SafeHtml
protected String name;

for update attempt with value <script>alert(123)</script> in controller

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

or

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

is thrown BindException for binding and MethodArgumentNotValidException for @RequestBody with default message:

name may have unsafe html content

Validator works as well for binding, as before persisting.
Apps could be tested at http://topjava.herokuapp.com/

UPDATE: see also comment from @GuyT

CVE-2019-10219 and status of @SafeHtml

We have been made aware of a CVE-2019-10219 related to the @SafeHtml constraint and it was fixed in both 6.0.18.Final and 6.1.0.Final....

However, we came to the conclusion that the @SafeHtml constraint was fragile, highly security-sensitive and depending on an external library that wasn’t designed for this purpose. Having it included in core Hibernate Validator was not a very good idea. That’s why we deprecated it and marked it for removal.There is no magic plan here so our users will have to maintain this constraint themselves

Resume for myself: it is safe and could be used, until solution better be found.

UPDATE: due to remove @SafeHtml/SafeHtmlValidator from hibernate.validator use own NoHtmlValidator, see https://stackoverflow.com/a/68888601/548473

提赋 2024-08-26 16:53:07

当您尝试防止 XSS 时,考虑上下文非常重要。例如,如果您在 JavaScript 代码片段中的变量内输出数据,而不是在 HTML 标记或 HTML 属性中输出数据,那么如何转义以及转义什么内容是非常不同的。

我有这里的一个例子。另请查看OWASP XSS 预防备忘单

所以简短的答案是,确保按照 Tendayi Mawushe 的建议转义输出,但在 HTML 属性或 JavaScript 中输出数据时要特别小心。

When you are trying to prevent XSS, it's important to think of the context. As an example how and what to escape is very different if you are ouputting data inside a variable in a JavaScript snippet as opposed to outputting data in an HTML tag or an HTML attribute.

I have an example of this here. Also checkout the OWASP XSS Prevention Cheat Sheet.

So the short answer is, make sure you escape output like suggested by Tendayi Mawushe, but take special care when you are outputting data in HTML attributes or JavaScript.

恰似旧人归 2024-08-26 16:53:07

您首先如何收集用户输入?如果您使用的是 FormController,此问题/答案可能会有所帮助:

Spring:绑定到命令时转义输入

How are you collecting user input in the first place? This question / answer may assist if you're using a FormController:

Spring: escaping input when binding to command

一人独醉 2024-08-26 16:53:07

始终手动检查您使用的方法、标签,并确保它们最终总是逃逸(一次)。框架在这方面有很多错误和差异。

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

Always check manually the methods, tags you use, and make sure that they always escape (once) in the end. Frameworks have many bugs and differences in this aspect.

An overview: http://www.gablog.eu/online/node/91

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文