XSS防御-使用AntiSamy

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

1、maven依赖

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


<dependency>
  <groupId>org.owasp.antisamygroupId>
  <artifactId>antisamyartifactId>
  <version>1.5.5version>
dependency>

2、策略文件

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

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

XSS防御-使用AntiSamy_第1张图片

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

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

1)、directives

全局配置,对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" />

  
  <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属性进行引用即可


<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>
    
    <attribute name="id"/>
    <attribute name="style"/>
    <attribute name="title"/>
    <attribute name="class"/>
    
    <attribute name="lang"/>
global-tag-attributes>

5)、tags-to-encode

需要进行编码处理的标签

<tags-to-encode>
  <tag>gtag>
  <tag>grintag>
tags-to-encode>

6)、tag-rules

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

  • remove

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

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

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

    如标题只保留标签和值

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

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

    <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包中几个常见策略文件

3、使用

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

// 需要过滤的数据
String taintedHTML = "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


<filter>
  <filter-name>XSSFilterfilter-name >
  <filter-class>cn.ghr.ehr.filter.XssFilterfilter-class >
  <init-param>
    <param-name>encodingparam-name>
    <param-value>UTF-8param-value>
  init-param>
filter>

<filter-mapping>
  <filter-name>XSSFilterfilter-name >
  <url-pattern>/url-pattern>
filter-mapping>

你可能感兴趣的:(Web安全)