前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >XSS攻击及AntiSamy防御

XSS攻击及AntiSamy防御

作者头像
有一只柴犬
发布2024-01-25 11:17:08
2240
发布2024-01-25 11:17:08
举报
文章被收录于专栏:JAVA体系JAVA体系
什么是xss

xss:跨站脚本攻击(Cross Site Scripting),因为跟样式css混淆,所以习惯缩写为xss。通过一些方法注入恶意指令代码到网页,使其加载并执行攻击者恶意的网页程序。

xss类型

1、反射型xss:通过get或者post等方式,向服务端输入数据。如果服务端不进行处理(过滤,验证,编码等),直接将信息呈现出来,可能会造成反射型xss。 2、存储型xss:服务端对注入的恶意脚本没有经过验证存入数据库,每次调用数据库都会将其渲染在浏览器上。则可能为存储型xss。

AntiSamy防御

主要思路为:对用户输入的脚本,提交的数据进行转义,编码。 AntiSamy提供了对恶意指令的过滤,各个标签、属性的处理方法。主要通过定义策略文件来达到防御的效果。

1、导入jar
代码语言:javascript
复制
<dependency>
  <groupId>org.owasp.antisamy</groupId>
  <artifactId>antisamy</artifactId>
  <version>1.5.5</version>
</dependency>
2、定义策略文件antisamy-slashdot.xml
代码语言:javascript
复制
<?xml version="1.0" encoding="ISO-8859-1"?>
<anti-samy-rules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:noNamespaceSchemaLocation="antisamy.xsd">

    <!-- 全局配置,对AntiSamy的过滤验证规则、输入、输出的格式进行控制 -->
    <directives>
        <directive name="omitXmlDeclaration" value="true"/>
        <directive name="omitDoctypeDeclaration" value="true"/>
        <directive name="maxInputSize" value="500000"/>
        <directive name="useXHTML" value="true"/>
        <directive name="formatOutput" value="false"/>
        <directive name="preserveComments" value="true"/>
        <directive name="onUnknownTag" value="encode"/>
        <directive name="nofollowAnchors" value="true" />

        <directive name="embedStyleSheets" value="false"/>
    </directives>

    <!-- 公用的正则表达式 -->
    <common-regexps>
        <regexp name="htmlTitle"
                value="[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&amp;]*"/> <!-- force non-empty with a '+' at the end instead of '*' -->
        <regexp name="onsiteURL" value="([\p{L}\p{N}\\/\.\?=\#&amp;;\-_~]+|\#(\w)+)"/>
        <regexp name="offsiteURL"
                value="(\s)*((ht|f)tp(s?)://|mailto:)[\p{L}\p{N}]+[~\p{L}\p{N}\p{Zs}\-_\.@\#\$%&amp;;:,\?=/\+!\(\)]*(\s)*"/>

    </common-regexps>
    
    <!-- 通用属性需要满足的输入规则 -->
    <common-attributes>
        <attribute name="lang"
                   description="The 'lang' attribute tells the browser what language the element's attribute values and content are written in">
            <regexp-list>
                <regexp value="[a-zA-Z]{2,20}"/>
            </regexp-list>
        </attribute>

        <attribute name="title"
                   description="The 'title' attribute provides text that shows up in a 'tooltip' when a user hovers their mouse over the element">
            <regexp-list>
                <regexp name="htmlTitle"/>
            </regexp-list>
        </attribute>

        <attribute name="href" onInvalid="filterTag">
            <regexp-list>
                <regexp name="onsiteURL"/>
                <regexp name="offsiteURL"/>
            </regexp-list>
        </attribute>

        <attribute name="align"
                   description="The 'align' attribute of an HTML element is a direction word, like 'left', 'right' or 'center'">
            <literal-list>
                <literal value="center"/>
                <literal value="left"/>
                <literal value="right"/>
                <literal value="justify"/>
                <literal value="char"/>
            </literal-list>
        </attribute>

    </common-attributes>

    <!-- 标签默认属性遵守的规则 -->
    <global-tag-attributes>
        <attribute name="title"/>
        <attribute name="lang"/>
    </global-tag-attributes>

    <!-- 需要进行编码处理的标签 -->
    <tags-to-encode>
        <tag>g</tag>
        <tag>grin</tag>
    </tags-to-encode>


   <!-- 标签的处理规则:
      1、remove:对应标签直接删除
      2、truncate:对应标签进行缩短处理,删除所有属性,只保留标签和值
      3、validate:对应标签的属性进行验证,如果tag中有定义的验证规则,则执行该规则,如果没有定义,则按照<global-tag-attributes>定义的处理
   -->
    <tag-rules>

        <!-- Tags related to JavaScript -->

        <tag name="script" action="remove"/>
        <tag name="noscript" action="remove"/>

        <!-- Frame & related tags -->

        <tag name="iframe" action="remove"/>
        <tag name="frameset" action="remove"/>
        <tag name="frame" action="remove"/>
        <tag name="noframes" action="remove"/>

        <!-- CSS related tags -->
        <tag name="style" action="remove"/>

        <!-- All reasonable formatting tags -->

        <tag name="p" action="validate">
            <attribute name="align"/>
        </tag>

        <tag name="div" action="validate"/>
        <tag name="i" action="validate"/>
        <tag name="b" action="validate"/>
        <tag name="em" action="validate"/>
        <tag name="blockquote" action="validate"/>
        <tag name="tt" action="validate"/>
        <tag name="strong" action="validate"/>

        <tag name="br" action="truncate"/>

        <!-- Custom Slashdot tags, though we're trimming the idea of having a possible mismatching end tag with the endtag="" attribute -->

        <tag name="quote" action="validate"/>
        <tag name="ecode" action="validate"/>


        <!-- Anchor and anchor related tags -->

        <tag name="a" action="validate">

            <attribute name="href" onInvalid="filterTag"/>
            <attribute name="nohref">
                <literal-list>
                    <literal value="nohref"/>
                    <literal value=""/>
                </literal-list>
            </attribute>
            <attribute name="rel">
                <literal-list>
                    <literal value="nofollow"/>
                </literal-list>
            </attribute>
        </tag>

        <!-- List tags -->

        <tag name="ul" action="validate"/>
        <tag name="ol" action="validate"/>
        <tag name="li" action="validate"/>

    </tag-rules>


    <!--  No CSS on Slashdot posts -->
    <!-- css处理规则 -->
    <css-rules>
    </css-rules>

</anti-samy-rules>

AntiSamy的策略文件类型有如下: **antisamy-anythinggoes.xml:**允许所有有效的html和css的输入(但能拒绝JavaScript或跟CSS相关的网络钓鱼攻击)。一般不建议使用。 **antisamy-ebay.xml:**允许任何人发布一系列富html的内容。适用于电商网站。 **antisamy-myspace.xml:**允许提交除了javascript之外的几乎所有的html和css。不建议使用。 **antisamy-slashdot.xml:**只允许提交b、u、i、a、blockquote的标签,不支持css。适用于新闻网站的评论过滤。 **antisamy-tinymce.xml:**只允许文本格式通过。 **antisamy.xml:**默认规则,允许大部分html通过。

3、定义xss过滤器
代码语言:javascript
复制
package com.yllt.common.filter.front;

import com.alibaba.fastjson.JSON;
import com.yllt.common.util.CollectionUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class XssFilter implements Filter {

    private static Logger log = LoggerFactory.getLogger(XssFilter.class);
    /**
     * 可放行的请求路径
     */

    private static final String IGNORE_PATH = "ignorePath";
    /**
     * 可放行的参数值
     */
    private static final String IGNORE_PARAM_VALUE = "ignoreParamValue";
    /**
     * 默认放行单点登录的登出响应(响应中包含samlp:LogoutRequest标签,直接放行)
     */
    private static final String CAS_LOGOUT_RESPONSE_TAG = "samlp:LogoutRequest";
    /**
     * 可放行的请求路径列表
     */
    private List<String> ignorePathList;
    /**
     * 可放行的参数值列表
     */
    private List<String> ignoreParamValueList;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("XSS fiter [XSSFilter] init start ...");
        String ignorePaths = filterConfig.getInitParameter(IGNORE_PATH);
        String ignoreParamValues = filterConfig.getInitParameter(IGNORE_PARAM_VALUE);
        if (!StringUtils.isBlank(ignorePaths)) {
            String[] ignorePathArr = ignorePaths.split(",");
            ignorePathList = Arrays.asList(ignorePathArr);
        }
        if (!StringUtils.isBlank(ignoreParamValues)) {
            String[] ignoreParamValueArr = ignoreParamValues.split(",");
            ignoreParamValueList = Arrays.asList(ignoreParamValueArr);
            //默认放行单点登录的登出响应(响应中包含samlp:LogoutRequest标签,直接放行)
            if (!ignoreParamValueList.contains(CAS_LOGOUT_RESPONSE_TAG)) {
                ignoreParamValueList.add(CAS_LOGOUT_RESPONSE_TAG);
            }
        } else {
            //默认放行单点登录的登出响应(响应中包含samlp:LogoutRequest标签,直接放行)
            ignoreParamValueList = new ArrayList<String>();
            ignoreParamValueList.add(CAS_LOGOUT_RESPONSE_TAG);
        }
        log.info("ignorePathList=" + JSON.toJSONString(ignorePathList));
        log.info("ignoreParamValueList=" + JSON.toJSONString(ignoreParamValueList));
        log.info("XSS fiter [XSSFilter] init end");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        log.info("XSS fiter [XSSFilter] starting");
        // 判断uri是否包含项目名称
        String uriPath = ((HttpServletRequest) request).getRequestURI();
        if (isIgnorePath(uriPath)) {
            log.info("ignore xssfilter,path[" + uriPath + "] pass through XssFilter, go ahead...");
            chain.doFilter(request, response);
            return;
        } else {
            log.info("has xssfiter path[" + uriPath + "] need XssFilter, go to XssRequestWrapper");
            chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request, ignoreParamValueList), response);
        }
        log.info("XSS fiter [XSSFilter] stop");
    }

    @Override
    public void destroy() {
        log.info("XSS fiter [XSSFilter] destroy");
    }

    private boolean isIgnorePath(String servletPath) {
        if (StringUtils.isBlank(servletPath)) {
            return true;
        }
        if (CollectionUtil.isListNULL(ignorePathList)) {
            return false;
        } else {
            for (String ignorePath : ignorePathList) {
                if (!StringUtils.isBlank(ignorePath) && servletPath.contains(ignorePath.trim())) {
                    return true;
                }
            }
        }

        return false;
    }
}

做一个装饰器类,来处理request的参数

代码语言:javascript
复制
package com.yllt.common.filter.front;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.List;
import java.util.Map;
import static com.yllt.common.filter.front.XssUtils.xssClean;


/**
 * @author asus
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private static Logger log = LoggerFactory.getLogger(XssHttpServletRequestWrapper.class);

    private List<String> ignoreParamValueList;

    public XssHttpServletRequestWrapper(HttpServletRequest request, List<String> ignoreParamValueList) {
        super(request);
        this.ignoreParamValueList = ignoreParamValueList;
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> requestMap = super.getParameterMap();
        for (Map.Entry<String, String[]> me : requestMap.entrySet()) {
            log.info(me.getKey() + ":");
            String[] values = me.getValue();
            for (int i = 0; i < values.length; i++) {
                log.info(values[i]);
                values[i] = xssClean(values[i], this.ignoreParamValueList);
            }
        }
        return requestMap;
    }

    @Override
    public String[] getParameterValues(String paramString) {
        String[] arrayOfString1 = super.getParameterValues(paramString);
        if (arrayOfString1 == null) {
            return null;
        }
        int i = arrayOfString1.length;
        String[] arrayOfString2 = new String[i];
        for (int j = 0; j < i; j++) {
            arrayOfString2[j] = xssClean(arrayOfString1[j], this.ignoreParamValueList);
        }
        return arrayOfString2;
    }

    @Override
    public String getParameter(String paramString) {
        String str = super.getParameter(paramString);
        if (str == null) {
            return null;
        }
        return xssClean(str, this.ignoreParamValueList);
    }

    @Override
    public String getHeader(String paramString) {
        String str = super.getHeader(paramString);
        if (str == null) {
            return null;
        }
        return xssClean(str, this.ignoreParamValueList);
    }
}

xss工具类,用来进行过滤处理

代码语言:javascript
复制
package com.yllt.common.filter.front;

import com.yllt.common.util.CollectionUtil;
import com.yllt.common.util.StringUtil;
import org.owasp.validator.html.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * XSS 工具类, 用于过滤特殊字符
 *
 * @author zuihou
 * @date 2019/07/02
 */
public class XssUtils {
    private static Logger log = LoggerFactory.getLogger(XssUtils.class);
    private static final String ANTISAMY_SLASHDOT_XML = "antisamy-slashdot-1.4.4.xml";
    private static Policy policy = null;

    static {
        log.info(" start read XSS configfile [" + ANTISAMY_SLASHDOT_XML + "]");
        InputStream inputStream = XssUtils.class.getClassLoader().getResourceAsStream(ANTISAMY_SLASHDOT_XML);
        try {
            policy = Policy.getInstance(inputStream);
            log.info("read XSS configfile [" + ANTISAMY_SLASHDOT_XML + "] success");
        } catch (PolicyException e) {
            log.error("read XSS configfile [" + ANTISAMY_SLASHDOT_XML + "] fail , reason:", e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("close XSS configfile [" + ANTISAMY_SLASHDOT_XML + "] fail , reason:", e);
                }
            }
        }
    }

    /**
     * 跨站攻击语句过滤 方法
     *
     * @param paramValue           待过滤的参数
     * @param ignoreParamValueList 忽略过滤的参数列表
     * @return
     */
    public static String xssClean(String paramValue, List<String> ignoreParamValueList) {
        AntiSamy antiSamy = new AntiSamy();

        try {
            log.info("raw value before xssClean: " + paramValue);
            if (isIgnoreParamValue(paramValue, ignoreParamValueList)) {
                log.info("ignore the xssClean,keep the raw paramValue: " + paramValue);
                return paramValue;
            } else {
                final CleanResults cr = antiSamy.scan(paramValue, policy);
                for(String msg : cr.getErrorMessages()){
                    log.info(msg);
                }
//                cr.getErrorMessages().forEach(log::debug);
                String str = cr.getCleanHTML();
                /*String str = StringEscapeUtils.escapeHtml(cr.getCleanHTML());
                str = str.replaceAll((antiSamy.scan("&nbsp;", policy)).getCleanHTML(), "");
                str = StringEscapeUtils.unescapeHtml(str);*/
                str = str.replaceAll("&quot;", "\"");
                str = str.replaceAll("&amp;", "&");
                str = str.replaceAll("'", "'");
                str = str.replaceAll("'", "'");

                str = str.replaceAll("&lt;", "<");
                str = str.replaceAll("&gt;", ">");
                log.info("xssfilter value after xssClean" + str);

                return str;
            }
        } catch (ScanException e) {
            log.error("scan failed armter is [" + paramValue + "]", e);
        } catch (PolicyException e) {
            log.error("antisamy convert failed  armter is [" + paramValue + "]", e);
        }
        return paramValue;
    }

    private static boolean isIgnoreParamValue(String paramValue, List<String> ignoreParamValueList) {
        if (StringUtil.isBlank(paramValue)) {
            return true;
        }
        if (CollectionUtil.isListNULL(ignoreParamValueList)) {
            return false;
        } else {
            for (String ignoreParamValue : ignoreParamValueList) {
                if (paramValue.contains(ignoreParamValue)) {
                    return true;
                }
            }
        }
        return false;
    }
}
4、配置过滤器
代码语言:javascript
复制
    <filter>
		<filter-name>xssFilter</filter-name>
		<filter-class>com.yllt.common.filter.front.XssFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>xssFilter</filter-name>
		<url-pattern>/front/*</url-pattern>
	</filter-mapping>
验证

网页端提交如下数据:

代码语言:javascript
复制
<script>alert(666)</script>

查看xss过滤日志:

代码语言:javascript
复制
2020-10-20 14:42:39,863 INFO [com.yllt.common.filter.front.XssFilter] - <XSS fiter [XSSFilter] starting>
2020-10-20 14:42:39,864 INFO [com.yllt.common.filter.front.XssFilter] - <has xssfiter path[/search.jhtml] need XssFilter, go to XssRequestWrapper>
2020-10-20 14:42:39,866 INFO [com.yllt.common.filter.front.XssUtils] - <raw value before xssClean: <script>123>
2020-10-20 14:42:39,867 INFO [com.yllt.common.filter.front.XssUtils] - <出于安全的原因,标记script不被允许。此标记不应该影响输入的显示。>

已被过滤,同时因为配置规则script标签为remove,网页端直接删除该标签。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-01-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是xss
  • xss类型
  • AntiSamy防御
    • 1、导入jar
      • 2、定义策略文件antisamy-slashdot.xml
        • 3、定义xss过滤器
          • 4、配置过滤器
            • 验证
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档