防止XSS攻击的过滤器

import com.XX.utils.StringUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 防止XSS攻击的过滤器
 */
public class XssFilter implements Filter
{
    /**
     * 排除链接
     */
    public List<String> excludes = new ArrayList<>();

    /**
     * xss过滤开关
     */
    public boolean enabled = false;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
        String tempExcludes = filterConfig.getInitParameter("excludes");
        String tempEnabled = filterConfig.getInitParameter("enabled");
        if (StringUtils.isNotEmpty(tempExcludes))
        {
            String[] url = tempExcludes.split(",");
            for (int i = 0; url != null && i < url.length; i++)
            {
                excludes.add(url[i]);
            }
        }
        if (StringUtils.isNotEmpty(tempEnabled))
        {
            enabled = Boolean.valueOf(tempEnabled);
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException
    {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        if (handleExcludeURL(req, resp))
        {
            chain.doFilter(request, response);
            return;
        }
        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
        chain.doFilter(xssRequest, response);
    }

    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
    {
        if (!enabled)
        {
            return true;
        }
        if (excludes == null || excludes.isEmpty())
        {
            return false;
        }
        String url = request.getServletPath();
        for (String pattern : excludes)
        {
            Pattern p = Pattern.compile("^" + pattern);
            Matcher m = p.matcher(url);
            if (m.find())
            {
                return true;
            }
        }
        return false;
    }

    @Override
    public void destroy()
    {
    }
}

XSS过滤处理

import java.io.ByteArrayInputStream;
import java.io.IOException;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import com.XX.utils.StringUtils;
import com.XX.utils.html.EscapeUtil;

/**
 * XSS过滤处理
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
{
    /**
     * @param request
     */
    public XssHttpServletRequestWrapper(HttpServletRequest request)
    {
        super(request);
    }

    @Override
    public String[] getParameterValues(String name)
    {
        String[] values = super.getParameterValues(name);
        if (values != null)
        {
            int length = values.length;
            String[] escapseValues = new String[length];
            for (int i = 0; i < length; i++)
            {
                // 防xss攻击和过滤前后空格
                escapseValues[i] = EscapeUtil.clean(values[i]).trim();
            }
            return escapseValues;
        }
        return super.getParameterValues(name);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException
    {
        // 非json类型,直接返回
        if (!isJsonRequest())
        {
            return super.getInputStream();
        }

        // 为空,直接返回
        String json = IOUtils.toString(super.getInputStream(), "utf-8");
        if (StringUtils.isEmpty(json))
        {
            return super.getInputStream();
        }

        // xss过滤
        json = EscapeUtil.clean(json).trim();
        final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes("utf-8"));
        return new ServletInputStream()
        {
            @Override
            public boolean isFinished()
            {
                return true;
            }

            @Override
            public boolean isReady()
            {
                return true;
            }

            @Override
            public void setReadListener(ReadListener readListener)
            {
            }

            @Override
            public int read() throws IOException
            {
                return bis.read();
            }
        };
    }

    /**
     * 是否是Json请求
     * 
     * @param request
     */
    public boolean isJsonRequest()
    {
        String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
        return MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(header)
                || MediaType.APPLICATION_JSON_UTF8_VALUE.equalsIgnoreCase(header);
    }
}

了解网站存在XSS恶意代码

原因

如果网站中已经被恶意用户植入了XSS代码,这说明网站具有XSS漏洞,并已经遭到黑客利用。造成XSS漏洞的原因就是,攻击者的输入没有经过严格的控制,最终显示给来访的用户,攻击者通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java, VBScript, ActiveX, Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、个人网页内容、会话和cookie等各种内容。

后果

  • 1.网络钓鱼,盗取各类用户的账号。
  • 2.窃取用户Cookie,获取用户隐私,或者利用用户身份进一步执行操作。
  • 3.劫持用户(浏览器)会话,从而执行任意操作,例如进行非法转账、强制发表日志等。
  • 4.强制弹出广告页面,刷流量等。
  • 5.进行恶意操作,例如任意篡改页面信息,删除文章等,传播跨站脚本蠕虫,网页挂马等。
  • 6.进行基于大量的客户端攻击,如DDOS攻击。
  • 7.结合其它漏洞,如CSRF漏洞。
  • 8.进一步渗透网站。

解决方式

删除恶意XSS代码,并针对XSS漏洞存在位置进行修复。
总体修复方式:验证所有输入数据,有效检测攻击;对所有输出数据进行适当的编码,以防止任何已成功注入的脚本在浏览器端运行。具体如下 :

  • 1.输入验证:某个数据被接受为可被显示或存储之前,使用标准输入验证机制,验证所有输入数据的长度、类型、语法以及业务规则。
  • 2.输出编码:数据输出前,确保用户提交的数据已被正确进行entity编码,建议对所有字符进行编码而不仅局限于某个子集。
  • 3.明确指定输出的编码方式:不要允许攻击者为你的用户选择编码方式(如ISO 8859-1或 UTF 8)。
  • 4.注意黑名单验证方式的局限性:仅仅查找或替换一些字符(如"<" ">"或类似"script"的关键字),很容易被XSS变种攻击绕过验证机制。
  • 5.警惕规范化错误:验证输入之前,必须进行解码及规范化以符合应用程序当前的内部表示方法。请确定应用程序对同一输入不做两次解码。对客户端提交的数据进行过滤,一般建议过滤掉双引号(”)、尖括号(<、>)等特殊字符,或者对客户端提交的数据中包含的特殊字符进行实体转换,比如将双引号(”)转换成其实体形式",<对应的实体形式是<,<对应的实体形式是>。