xss漏洞修复

1,085 阅读4分钟

什么是xss攻击?

XSS攻击是Cross-Site Scripting的缩写,直白来说,就是页面被注入了恶意的代码——用户输入的内容跳出文本的限制,成了可执行的代码。

xss分类

根据入侵代码的来源,通常将XSS攻击分成三类

存储型xss攻击

特点:恶意代码已经落库,被拼接到HTML中返回。

  1. 攻击者通过论坛评论区提交恶意代码到数据库;
  2. 其他用户打开论坛,服务端把恶意代码取出来,拼接在HTML中返回;
  3. 恶意代码在用户的浏览器端运行;

除了论坛,这类攻击还常见于用户私信发送。

<div>
 评论内容:<%= getContent("comment") %>
</div>
<div>
  评论内容:<script>alert('XSS');</script>
</div>

反射性xss攻击

特点:恶意代码被拼接到URL上,被拼接到HTML中返回。

  1. 攻击者拼接出包含恶意代码的受害网站URL,诱导用户点击;
  2. 用户点击该URL,目标网站的服务器取出恶意代码,拼接到HTML返回;
  3. 恶意代码在其他用户的浏览器端运行;

这类攻击需要用户主动点击受害网站的URL,攻击者会通过通过QQ群或者邮件等方式诱导点击。 比如一个恶意URL可以长这样:http://xxx/search?keyword="<script>alert('XSS');</script>

<div>
  你好<%= getParameter("keyword") %>
</div>
<div>
  你好<script>alert('XSS');</script>
</div>

dom xss攻击

特点:恶意代码被拼接到URL上,被前端JavaScript代码执行。

  1. 攻击者拼接出包含恶意代码的受害网站URL,诱导用户点击;
  2. 用户点击该URL;
  3. 前端 JavaScript 取出 URL 中的恶意代码,恶意代码在用户的浏览器端运行;

DOM型和反射型的区别在于,DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞。而其他两种 XSS 都属于服务端的安全漏洞。

上述三种XSS攻击的目的都一致:恶意代码在浏览器端运行后,窃取用户的本地存储数据:通过document.cookie获取用户的身份凭证,然后通过网络请求将数据发送给恶意服务器

接下来就可以进行下一步:冒充用户去对受害网站发起请求完成指定操作,比如转账给攻击者的账户。

漏洞修复

既然xss漏洞是分为三种类型,那么理论上就按照对应类型来修复漏洞就可以了,但是那,仔细对比一下 上面所谓的存储型和反射型,似乎都是经过服务端返回的,那其实只要我们后端返回的时候,校验掉这些错误或者恶意代码就可以了,所以其实就是两种解决方案对应的 三种的漏洞类型。

第一种和第二种漏洞类型修复

其实都是通过filter来实现的,但是有意思的是 你使用filter是有两种方式的

  1. 直接继承filter类,实现对应的doFilter方法即可,当然要注入到spring容器当中
  2. 通过@bean来实现,由于springbootweb提供了FilterRegistrationBean,所以直接new一个注入进去即可

继承filter

pom文件

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-text</artifactId>
  <version>1.4</version>
</dependency>

java

XSSFilter
package com.example.scheduledtest;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
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.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.stereotype.Component;

@WebFilter
@Component
public class XSSFilter implements Filter {
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
  }

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    XssAndSqlHttpServletRequestWrapper xssRequestWrapper = new XssAndSqlHttpServletRequestWrapper(req);
    chain.doFilter(xssRequestWrapper, response);
  }

  @Override
  public void destroy() {
  }

  /**
   * 过滤json类型的
   *
   * @param builder
   * @return
   */
  @Bean
  @Primary
  public ObjectMapper xssObjectMapper(Jackson2ObjectMapperBuilder builder) {
    //解析器
    ObjectMapper objectMapper = builder.createXmlMapper(false).build();
    //注册xss解析器
    SimpleModule xssModule = new SimpleModule("XssStringJsonSerializer");
    xssModule.addSerializer(new XssStringJsonSerializer());
    objectMapper.registerModule(xssModule);
    //返回
    return objectMapper;
  }
}
XssAndSqlHttpServletRequestWrapper
package com.example.scheduledtest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.text.StringEscapeUtils;
import org.springframework.util.StringUtils;

public class XssAndSqlHttpServletRequestWrapper extends HttpServletRequestWrapper {
  private HttpServletRequest request;

  public XssAndSqlHttpServletRequestWrapper(HttpServletRequest request) {
    super(request);
    this.request = request;
  }

  @Override
  public String getParameter(String name) {
    String value = request.getParameter(name);
    if (!StringUtils.isEmpty(value)) {
      value = StringEscapeUtils.escapeHtml4(value);
    }
    return value;
  }

  @Override
  public String[] getParameterValues(String name) {
    String[] parameterValues = super.getParameterValues(name);
    if (parameterValues == null) {
      return null;
    }
    for (int i = 0; i < parameterValues.length; i++) {
      String value = parameterValues[i];
      parameterValues[i] = StringEscapeUtils.escapeHtml4(value);
    }
    return parameterValues;
  }
}
XssStringJsonSerializer
package com.example.scheduledtest;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import org.apache.commons.text.StringEscapeUtils;


public class XssStringJsonSerializer extends JsonSerializer<String> {
  @Override
  public Class<String> handledType() {
    return String.class;
  }

  @Override
  public void serialize(String value, JsonGenerator jsonGenerator,
      SerializerProvider serializerProvider) throws IOException {
    if (value != null) {
      String encodedValue = StringEscapeUtils.escapeHtml4(value);
      jsonGenerator.writeString(encodedValue);
    }
  }
}

第三种漏洞修复

新建cookie的时候,设置http-only为true就可以了。

cookie.setHttpOnly(true);

参考