XSS防御-使用AntiSamy

AntiSamy是OWASP的一个开源项目,通过对用户输入的 HTML / CSS / JavaScript 等内容进行检验和清理,确保输入符合应用规范。AntiSamy被广泛应用于Web服务对存储型和反射型XSS的防御中。

1、maven依赖

AntiSamy直接导入到工程即可,但是其运行依赖xercesImpl、batik、nekohtml,这些依赖默认会一起导入

<!-- OWASP AntiSamy -->
<dependency>
  <groupId>org.owasp.antisamy</groupId>
  <artifactId>antisamy</artifactId>
  <version>1.5.5</version>
</dependency>

2、策略文件

AntiSamy对“恶意代码”的过滤依赖于策略文件。策略文件规定了AntiSamy对各个标签、属性的处理方法,策略文件定义的严格与否,决定了AntiSamy对XSS漏洞的防御效果。

在AntiSamy的jar包中,包含了几个常用的策略文件


我们可以自定义策略文件来过滤用户输入,但更多的会是基于现有的策略文件进行稍微的调整,以使其更贴合项目的实际需求。

要描述某种特定规则,XML无疑是个不错的选择,而AntiSamy策略文件也正是采用了XML格式。如图所示,除去文件头的AntiSamy策略文件可以分为八个部分

1)、directives

扫描二维码关注公众号,回复: 2842538 查看本文章

全局配置

validate

对应的标签的属性进行验证,如果tag中定义了属性的验证规则,按照tag中的规则执行;如果标签中未定义属性,则按照 <gl

validate

对应的标签的属性进行验证,如果tag中定义了属性的验证规则,按照tag中的规则执行;如果标签中未定义属性,则按照 <global-tag-attributes> 中定义的处理

obal-tag-att
<tag name="head" action="validate"/>

ributes> 中定义的处理,对AntiSamy的过滤验证规则、输入及输出的格式进行全局性的控制
<directives>
  <directive name="omitXmlDeclaration" value="true"/>
  <directive name="omitDoctypeDeclaration" value="true"/>
  <directive name="maxInputSize" value="200000"/>
  <directive name="useXHTML" value="true"/>
  <directive name="formatOutput" value="true"/>
  <directive name="nofollowAnchors" value="true" />
  <directive name="validateParamAsEmbed" value="true" />

  <!--
  remember, this won't work for relative URIs - AntiSamy doesn't
  know anything about the URL or your web structure
  -->
  <directive name="embedStyleSheets" value="false"/> 
  <directive name="connectionTimeout" value="5000"/>
  <directive name="maxStyleSheetImports" value="3"/>

</directives>

2)、common-regexps

公用正则表达式,需要使用正则的时候可以通过name直接引用

<common-regexps>
  <regexp name="numberOrPercent" value="(\d)+(%{0,1})"/>
  <regexp name="paragraph" value="([\p{L}\p{N},'\.\s\-_\(\)\?]|&[0-9]{2};)*"/>  
  <regexp name="htmlId" value="[a-zA-Z0-9\:\-_\.]+"/>
</common-regexps>
假设后文需要使用”htmlId”这一正则时,直接根据对应的name属性进行引用即可
<!-- Common to all HTML tags  -->
<attribute name="id" description="The 'id' of any HTML attribute should not contain anything besides letters and numbers">
    <regexp-list>
        <!-- 直接根据正则的名称进行引用 -->
        <regexp name="htmlId"/>
    </regexp-list>
</attribute>

3)、common-attributes

通用的属性需要满足的输入规则,其中包括了标签和css的属性;在tag和css的处理规则中会引用到这些属性

<common-attributes>
  <attribute name="classid">
    <regexp-list>
        <regexp name="anything" />
    </regexp-list>
  </attribute>
  <attribute name="autocomplete">
    <literal-list>
      <literal value="on"/>
      <literal value="off"/>
    </literal-list>
  </attribute>
</common-attributes>

4)、global-tag-attributes

所有标签的默认属性需要遵守的规则

<global-tag-attributes>
    <!-- Not valid in base, head, html, meta, param, script, style, and title elements. -->
    <attribute name="id"/>
    <attribute name="style"/>
    <attribute name="title"/>
    <attribute name="class"/>
    <!-- Not valid in base, br, frame, frameset, hr, iframe, param, and script elements.  -->
    <attribute name="lang"/>
</global-tag-attributes>

5)、tags-to-encode

需要进行编码处理的标签

<tags-to-encode>
  <tag>g</tag>
  <tag>grin</tag>
</tags-to-encode>

6)、tag-rules

tag的处理规则,共有三种处理方式

  • remove

    对应的标签直接删除,如script标签处理规则为删除

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

truncate

对应的标签进行缩短处理,直接删除所有属性,只保留标签和值

如标题只保留标签和值

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

validate

对应的标签的属性进行验证,如果tag中定义了属性的验证规则,按照tag中的规则执行;如果标签中未定义属性,则按照 <global-tag-attributes> 中定义的处理


<tag name="head" action="validate"/>

7)、css-rules

CSS的处理规则

<css-rules>
  <property name="bottom" default="auto" description="">
    <category-list>
        <category value="visual"/>
    </category-list>
    <literal-list>
        <literal value="auto"/>
        <literal value="inherit"/>
    </literal-list>
    <regexp-list>
        <regexp name="length"/>
        <regexp name="percentage"/>
    </regexp-list>
  </property>
  <property name="color" description="">
    <category-list>
        <category value="visual"/>
    </category-list>
    <literal-list>
        <literal value="inherit"/>
    </literal-list>
    <regexp-list>
        <regexp name="colorName"/>
        <regexp name="colorCode"/>
        <regexp name="rgbCode"/>
        <regexp name="systemColor"/>
    </regexp-list>
  </property>
</css-rules>

8)、allowed-empty-tags

允许没有内容的标签

<allowed-empty-tags>
  <literal-list>
    <literal value="br"/>
    <literal value="hr"/>
    <literal value="a"/>
    <literal value="img"/>
    <literal value="link"/>
    <literal value="iframe"/>
    <literal value="script"/>
    <literal value="object"/>
    <literal value="applet"/>
    <literal value="frame"/>
    <literal value="base"/>
    <literal value="param"/>
    <literal value="meta"/>
    <literal value="input"/>
    <literal value="textarea"/>
    <literal value="embed"/>
    <literal value="basefont"/>
    <literal value="col"/>
    <literal value="div"/>
  </literal-list>
</allowed-empty-tags>

大概清楚了每个标签代表的意义,便能够很容易地写出符合自己需求的策略文件了。我们再来简单的看下jar包中几个常见策略文件

  • antisamy-anythinggoes.xml

    允许所有有效的HTML和CSS元素输入(但能拒绝JavaScript或跟CSS相关的网络钓鱼攻击),因为它包含了对于每个元素的基本规则,所以你在裁剪其它策略文件的时候可以把它作为一个知识库,一般不建议使用。

  • antisamy-ebay.xml

    eBay 是当下最流行的在线拍卖网站之一。它是一个面向公众的站点,因此它允许任何人发布一系列富HTML的内容,允许输入的内容列表包含了比 Slashdot 更多的富文本内容,所以它的受攻击面也要大得多。

    该策略相对安全,适用于电子商务网站。

  • antisamy-myspace.xml

    MySpace 是最流行的一个社交网站之一。用户允许提交除了JavaScript之外的几乎所有他们想用的HTML和CSS。 
    MySpace现在用一个黑名单来验证用户输入的HTML,相对较危险,不建议使用。

  • antisamy-slashdot.xml

    Slashdot 是一个提供技术新闻的网站,其安全策略非常严格。用户只能提交下列的HTML标签:<b>、<u>、<i>、<a>、<blockquote>,并且还不支持CSS。

    该策略文件来实现了类似的功能,允许所有文本格式的标签来直接修饰字体、颜色或者强调作用,适用于新闻网站的评论过滤。

  • antisamy-tinymce.xml

    只允许文本格式通过,相对较安全

  • antisamy.xml

    默认规则,允许大部分HTML通过

3、使用

其实 AntiSamy 在使用上是十分简单的,指定策略文件后构建AntiSamy对象,然后将数据传入AntiSamy对象进行过滤即可

// 需要过滤的数据
String taintedHTML = "<script>alert(\"xss\");</script>HELLO WORD!";

// 根据策略文件创建过滤策略
Policy policy = Policy.getInstance( "antisamy-ebay.xml");

// 根据策略对数据进行过滤
AntiSamy antiSamy = new AntiSamy();
CleanResults cr = antiSamy.scan( taintedHTML, policy);  
taintedHTML = cr.getCleanHTML();

对于一个项目来说,基本每个输入都需要进行检查,所以一般我们会结合Filter进行使用。

Filter是一个典型的过滤链,可以用来对 HttpServletRequest 进行预处理,或者是对 HttpServlerResponse 进行后处理。

定义自定义过滤器 XssFilter 类,实现 Filter 接口,并对 doFilter( ServletRequest request, ServletResponse response, FilterChain chain) 方法进行重写。为了对用户请求进行处理,需要重写 ServletRequest ,并交由 FilterChain 执行

/**
 * XSS (Cross Site Scripting) 过滤器
 * @author zhangcs
 */
public class XssFilter implements Filter{

    @SuppressWarnings("unused")
    private FilterConfig filterConfig;

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 传入重写后的Request
        chain.doFilter( new XssRequestWrapper( ( HttpServletRequest)request), response);
    }

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

}

新建一个自定义 HttpServletRequest 包装类 XssRequestWrapper ,继承 HttpServletRequestWrapper 类,并对getParameter( String param)getParameterValues( String param)以及 getHeader( String param) 等方法进行重写。

例如我们使用的MVC框架为SpringMVC,需要对用户输入的参数值进行过滤,则重写 getParameterValues( String name) 方法

/**
 * 自定义的XSS请求包装器
 * @author zhangcs
 */
public class XssRequestWrapper extends HttpServletRequestWrapper {

    public XssRequestWrapper( HttpServletRequest request) {
        super(request);
    }

    @Override
    public String[] getParameterValues( String name){  

        String[] values = super.getParameterValues( name);  
        if ( values == null){
            return null;  
        }

        int len = values.length;
        String[] newArray = new String[len];
        for (int j = 0; j < len; j++){

            // 过滤
            newArray[j] = xssClean( values[j]);
        }

        return newArray;
    }

    /** 
     * 策略文件
     * 注意,需要将要使用的策略文件放到项目资源文件路径下 
     * */
    private static String antiSamyPath = XssRequestWrapper.class.getClassLoader()
            .getResource( "antisamy-ebay.xml").getFile();

    /**
     * AntiSamy过滤数据
     * @param taintedHTML 需要进行过滤的数据
     * @return 返回过滤后的数据
     * */
    private String xssClean( String taintedHTML){  

        try{
            // 指定策略文件
            Policy policy = Policy.getInstance( antiSamyPath);

            // 使用AntiSamy进行过滤
            AntiSamy antiSamy = new AntiSamy(); 
            CleanResults cr = antiSamy.scan( taintedHTML, policy);  
            taintedHTML = cr.getCleanHTML();
        }catch( ScanException e) {  
            e.printStackTrace();
        }catch( PolicyException e) {  
            e.printStackTrace();
        }

        return taintedHTML;
    }

}
Filter对象将会在web应用启动时由服务器根据  web.xml  文件中的配置信息来创建,所以还需要在  web.xml  文件中配置注册自定义的  XssFilter
<!-- 注册XssFilter -->
<filter>
  <filter-name>XSSFilter</filter-name >
  <filter-class>cn.ghr.ehr.filter.XssFilter</filter-class >
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
</filter>
<!-- 配置需要过滤的请求 -->
<filter-mapping>
  <filter-name>XSSFilter</filter-name >
  <url-pattern>/</url-pattern>
</filter-mapping>

转载
http://blog.csdn.net/qq_35946990/article/details/74982760


猜你喜欢

转载自blog.csdn.net/u012965203/article/details/79240274