当前位置:   article > 正文

Web 安全头号大敌 XSS 漏洞解决最佳实践_esapi xss

esapi xss

引言

XSS 是目前最普遍的 Web 应用安全漏洞,它带来的危害是巨大的,是 Web 安全的头号大敌。

 

关键词

  • 跨站

  • 脚本(JavaScript、Java、 VBScript、ActiveX、 Flash 或者 HTML)

  • 注入

  • 执行

 

1. 什么是 XSS 漏洞?

XSS 攻击:跨站脚本攻击(Cross Site Scripting),为不和 前端层叠样式表(Cascading Style Sheets)CSS 混淆,故将跨站脚本攻击缩写为 XSS。

XSS(跨站脚本攻击)是指恶意攻击者往 Web 页面里插入恶意 Script 代码,当用户浏览该页时,嵌入其中 Web 里面的 Script 代码会被执行,从而达到恶意攻击用户的目的。类似于 sql 注入。是目前最普遍的 Web 应用安全漏洞,也是 Web 攻击中最常见的攻击方式之一。

XSS( 跨站脚本攻击)攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是 JavaScript,但实际上也可以包括 Java、 VBScript、ActiveX、 Flash 或者甚至是普通的 HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和 cookie 等各种内容。

 

2. XSS 漏洞攻击原理及攻击手段

HTML 是一种超文本标记语言,通过将一些字符特殊地对待来区别文本和标记,例如,小于符号(<)被看作是 HTML 标签的开始,之间的字符是页面的标题等等。

当动态页面中插入的内容含有这些特殊字符(如<)时,用户浏览器会将其误认为是插入了 HTML 标签,当这些 HTML 标签引入了一段 JavaScript 脚本时,这些脚本程序就将会在用户浏览器中执行。所以,当这些特殊字符不能被动态页面检查或检查出现失误时,就将会产生 XSS 漏洞。

常用的 XSS 攻击手段和目的有:

  1. 1、盗用 cookie,获取敏感信息。
  2. 2、利用植入 Flash,通过 crossdomain 权限设置进一步获取更高权限;或者利用 Java 等得到类似的操作。
  3. 3、利用 iframe、frame、XMLHttpRequest 或上述 Flash 等方式,以(被攻击)用户的身份执行一些管理动作,或执行一些一般的如发微博、加好友、发私信等操作。
  4. 4、利用可被攻击的域受到其他域信任的特点,以受信任来源的身份请求一些平时不允许的操作,如进行不当的投票活动。
  5. 5、在访问量极大的一些页面上的 XSS 可以攻击一些小型网站,实现 DDoS 攻击的效果。

最简单的 XSS 示例

  1. <script>alert('hello world')</script>
  2. <script src='http://www.smart4j.cn/xxx.js'></script>
  3. <input type="button" value="评论"></input>

 

3. XSS 分类如下:

分类

主要特点

存储型 XSS

经过后端服务处理,数据存储在数据库端

反射型 XSS

经过后端服务处理,不存储数据库

DOM型 XSS

不经过后端服务处理,不存储数据库

 

4. XSS 漏洞分析

4.1 存储型 XSS

通过网页注入的代码最终会存储在数据库或其他物理文件中,在某个页面中注入的代码会被浏览器成功执行,该类型的漏洞存在持久性的特点。

主要特点:

  • 存储

  • 持久性

图解成因分析:

4.2 反射型 XSS

一般是通过 url 的形式注入代码,注入的代码不在服务器端存储,但会在服务器端进行处理然后进行回显,在回显时浏览器会触发漏洞执行注入代码,该类型攻击具有临时性特点。

主要特点:

  • 回显

  • 临时性

图解成因分析

4.3 DOM 型 XSS

也是通过 url 的形式注入代码,注入的代码服务器端程序不存储、不处理,而是由浏览器进行处理,该类型攻击也具有临时性特点。主要特点:

  • 不存储、不处理

  • 浏览器

  • 临时性

图解成因分析

 

5. 三种XSS漏洞对比

分类

一般表现形式

特点

存储型 XSS

表现为包含表单的页面,post提交后数据存储在数据库,通过其他页面访问触发

存储、持久性

反射型XSS

表现为包含参数的url地址,参数经后端程序程序处理后回显,通过访问 url 触发

url参数、后端处理参数、临时性

DOM型XSS

表现为包含参数的url地址,参数由页面中的JS代码处理,通过访问 url 触发

url参数、JS处理参数、临时性

6. 植入 JS 代码攻击及危害分析

外在表现形式:

  • 直接注入 JavaScript 代码

  • 引用外部 JS 文件

基本实现原理:

  • 通过 img 标签的 src 发送数据

  • 构造表单诱导用户输入账密

  • 构造隐藏的 form 表单自动提交

  • 页面强制跳转

  • 植入文字链接、图片链接

潜在危害:

  • 获取管理员或者其他用户 Cookie,冒充用户身份登录

  • 构造表单诱导用户输入账号、密码,获取账密

  • 跳转到其他网站,网站流量被窃取

  • 植入广告、外链等

  • 通过隐藏友链提升其他网站百度权重(SEO 黑帽)

 

7. 植入 HTML 代码攻击及危害分析

外在表现形式:

  • 构造 img 标签

  • 构造 a 标签

  • 构造其他 HTML 标签

基本实现原理:

  • 通过 img 标签的 src 发送数据

  • 通过 img 的 onerror 触发脚本代码

  • 通过 a 标签被动触发脚本代码 href/onclick

  • 通过 iframe 引入第三方页面

  • 直接构造文字链接或图片链接

  • style 属性嵌入脚本代码  background-image:url("javascript:…");(浏览器已可防范)

潜在危害:

  • 获取管理员或者其他用户 Cookie,冒充用户身份登录

  • 构造表单诱导用户输入账号、密码,获取账密

  • 植入广告、外链等

  • 通过隐藏友链提升其他网站百度权重(SEO 黑帽)

 

8. XSS 简单预防策略

8.1 对 html 标签进行字符替换:

  1. replaceAll("<script", "");
  2. ...

存在问题:

    大小写问题

优化升级:

    正则表达式

存在问题:

    反替换<scr<scriptipt ——> <script

8.2 对 html 字符转义或是半角转全角字符:

  1. { "<", ">", "\""}
  2. 转义:
  3. { "&lt;", "&gt;", "&quot;"})
  4. 全角:
  5. { "<, "", "\""}

新场景:

    页面中需要根据某个参数生成文字链接

存在问题:

    a 标签的 href 属性 javascript:

其他问题:

    针对 json 字符串的场景

    如何公用的问题

 

9. XSS 漏洞预防策略最佳实践

9.1 输入环节

  • 页面限制输入长度、特殊字符限制,后端代码限制输入长度、处理特殊字符

  • Filter 过滤器统一处理(自定义处理规则、使用 Apache Text、使用 Owasp AntiSamy)

9.2 Cookie 防护

  • cookie 设置 httponly,一般 servlet 容器默认 httponly 为 true

  • resp.setHeader("SET-COOKIE", "JSESSIONID=" + request.getSession().getId()+ "; HttpOnly");

9.3 X-Frame-Options 响应头 (是否允许frame、iframe等标记)

  • DENY 不允许、SAMEORIGIN 可在相同域名页面的 frame 中展示、ALLOW-FROM uri 可在指定页的 frame 中展示

  • add_header X-Frame-Options SAMEORIGIN; //在nginx的 http 或 server 节点中配置即可

  • 也可通过 Filter 设置  resp.setHeader("x-frame-options","SAMEORIGIN");

9.4 输出环节

  • OWASP ESAPI for Java

  • 显示时对字符进行转义处理,各种模板都有相关语法,注意标签的正确使用

    示例如下:

thymeleaf

  1. <span th:utext="${ result }"></span>---><span th:text="${ result }"></span>
  2. <!-- utext 与 text 区别 -->

JSP

<c:out value=" ${ content }"  escapeXml="false" />---><c:out value=" ${ content }"/> <!-- escapeXml默认true -->

9.5 DOM 型 XSS

  • 避免 .innerHTML、.outerHTML、document.write() 的使用,应使用 .textContent、.setAttribute() 等。

  • Vue/React 技术栈,避免使用 v-html/dangerouslySetInnerHTML

  • 尤其注意 onclick、onerror、onload、onmouseover 、eval()、setTimeout()、setInterval() 以及 a 标签的 href

  • 可使用 OWASP  esapi4js : esapi.js

 

10. 后端服务编码实践

通过 https://start.spring.io/ 快速创建 springboot 应用:

解压并在 IDEA 导入刚刚创建的 xss-demo 项目

在 pom.xml 添加相关依赖:

  1. <!--防止XSS攻击的antiSamy-->
  2. <dependency>
  3.      <groupId>org.owasp.antisamy</groupId>
  4.      <artifactId>antisamy</artifactId>
  5.      <version>1.5.7</version>
  6. </dependency>
  7. <!--fastjson依赖-->
  8. <dependency>
  9.     <groupId>com.alibaba</groupId>
  10.     <artifactId>fastjson</artifactId>
  11.     <version>1.2.62</version>
  12. </dependency>

修改 xss-demo 工程包结构如下:

XSSFilter 编码如下:

/** * @program: xss-demo * @author: Mr.Zhang * @create: 2021-02-21 15:45 **/public class XssFilter implements Filter {    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {        HttpServletRequest httpServletRequest = (HttpServletRequest) request;        HttpServletRequest req = (HttpServletRequest) request;        String path = req.getServletPath();        //注解配置的是urlPatterns="/*"(过滤所有请求),所以这里对不需要过滤的静态资源url,作忽略处理(大家可以依照具体需求配置)        String[] exclusionsUrls = {".js", ".gif", ".jpg", ".png", ".css", ".ico"};        for (String str : exclusionsUrls) {            if (path.contains(str)) {                chain.doFilter(request, response);                return;            }        }        chain.doFilter(new XssRequestWrapper(httpServletRequest), response);    }}

XssRequestWrapper 包装类编码如下:

  1. import com.alibaba.fastjson.JSON;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.owasp.validator.html.AntiSamy;
  4. import org.owasp.validator.html.CleanResults;
  5. import org.owasp.validator.html.Policy;
  6. import org.owasp.validator.html.PolicyException;
  7. import org.owasp.validator.html.ScanException;
  8. import javax.servlet.ReadListener;
  9. import javax.servlet.ServletInputStream;
  10. import javax.servlet.http.HttpServletRequest;
  11. import javax.servlet.http.HttpServletRequestWrapper;
  12. import java.io.BufferedReader;
  13. import java.io.ByteArrayInputStream;
  14. import java.io.IOException;
  15. import java.io.InputStream;
  16. import java.io.InputStreamReader;
  17. import java.util.Map;
  18. /**
  19. * @program: xss-demo
  20. * @author: Mr.Zhang
  21. * @create: 2021-02-21 15:46
  22. **/
  23. @Slf4j
  24. public class XssRequestWrapper extends HttpServletRequestWrapper {
  25. public XssRequestWrapper(HttpServletRequest request) {
  26. super(request);
  27. }
  28. /**
  29. * 获取策略文件,直接使用jar中自带的ebay策略文件
  30. */
  31. private static InputStream inputStream = XssRequestWrapper.class.getClassLoader()
  32. .getResourceAsStream("antisamy-ebay.xml");
  33. private static Policy policy = null;
  34. static {
  35. try {
  36. // 使用静态代码块处理策略对象的创建
  37. policy = Policy.getInstance(inputStream);
  38. } catch (PolicyException e) {
  39. e.printStackTrace();
  40. }
  41. }
  42. /**
  43. * 使用AntiSamy进行过滤数据
  44. * @param html
  45. * @return
  46. */
  47. private String xssClean(String html) {
  48. String cleanHTML = "";
  49. try {
  50. AntiSamy antiSamy = new AntiSamy();
  51. CleanResults scan = antiSamy.scan(html, policy);
  52. cleanHTML = scan.getCleanHTML();
  53. } catch (ScanException e) {
  54. e.printStackTrace();
  55. } catch (PolicyException e) {
  56. e.printStackTrace();
  57. }
  58. return cleanHTML;
  59. }
  60. /**
  61. * 重写处理请求参数的方法
  62. * @param name
  63. * @return
  64. */
  65. @Override
  66. public String[] getParameterValues(String name) {
  67. String[] values = super.getParameterValues(name);
  68. // 判断参数有值,如果没有值,直接返回
  69. if (values == null) {
  70. return null;
  71. }
  72. // 遍历参数数组,使用AntiSamy进行过滤
  73. int len = values.length;
  74. String[] newValues = new String[len];
  75. for (int i = 0; i < len; i++) {
  76. // 过滤前的数据
  77. log.info("使用AntiSamy进行过滤清理,过滤清理之前的数据:{}", values[i]);
  78. // 进行过滤
  79. newValues[i] = xssClean(values[i]);
  80. // 过滤后的数据
  81. log.info("使用AntiSamy进行过滤清理,过滤清理之后的数据:{}", newValues[i]);
  82. }
  83. //返回过滤后的结果
  84. return newValues;
  85. }
  86. /**
  87. * 重写处理json数据的方法
  88. * @return
  89. * @throws IOException
  90. */
  91. @Override
  92. public ServletInputStream getInputStream() throws IOException {
  93. // 读取流
  94. BufferedReader reader = new BufferedReader(
  95. new InputStreamReader(super.getInputStream(), "UTF-8"));
  96. // 获取json格式的数据
  97. StringBuilder sb = new StringBuilder();
  98. String inputStr;
  99. while ((inputStr = reader.readLine()) != null) {
  100. sb.append(inputStr);
  101. }
  102. // 把json转为map
  103. Map map = JSON.parseObject(sb.toString(), Map.class);
  104. // 过滤前
  105. log.info("json过滤前:{}", sb.toString());
  106. // 对map中的value值进行AntiSamy的过滤
  107. map.keySet().forEach(k -> {
  108. map.put(k, xssClean(map.get(k).toString()));
  109. });
  110. // 过滤后
  111. String json = JSON.toJSONString(map);
  112. log.info("json过滤后:{}", json);
  113. // 把json数据转为流的格式进行返回
  114. ByteArrayInputStream bais = new ByteArrayInputStream(json.getBytes());
  115. return new ServletInputStream() {
  116. @Override
  117. public boolean isFinished() {
  118. return false;
  119. }
  120. @Override
  121. public boolean isReady() {
  122. return false;
  123. }
  124. @Override
  125. public void setReadListener(ReadListener listener) { }
  126. @Override
  127. public int read() throws IOException {
  128. return bais.read();
  129. }
  130. };
  131. }
  132. }

AntiSamyConfig 配置过滤器类编码如下:

  1. import cn.smart4j.xssdemo.filter.XssFilter;
  2. import org.springframework.boot.web.servlet.FilterRegistrationBean;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. /**
  6. * @program: xss-demo
  7. * @author: Mr.Zhang
  8. * @create: 2021-02-21 15:58
  9. **/
  10. @Configuration
  11. public class AntiSamyConfig {
  12. /**
  13. * 配置xss过滤器
  14. * @return
  15. */
  16. @Bean
  17. public FilterRegistrationBean create() {
  18. FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new XssFilter());
  19. filterRegistrationBean.addUrlPatterns("/*");
  20. filterRegistrationBean.setOrder(1);
  21. return filterRegistrationBean;
  22. }
  23. }

User 实体类:

  1. import lombok.Data;
  2. /**
  3. * @program: xss-demo
  4. * @author: Mr.Zhang
  5. * @create: 2021-02-21 15:42
  6. **/
  7. @Data
  8. public class User {
  9. private int id;
  10. private String name;
  11. private int age;
  12. }

UserController 测试控制器类编码如下:

  1. import cn.smart4j.xssdemo.domain.User;
  2. import com.alibaba.fastjson.JSON;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.springframework.web.bind.annotation.PostMapping;
  5. import org.springframework.web.bind.annotation.RequestBody;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. /**
  9. * @program: xss-demo
  10. * @author: Mr.Zhang
  11. * @create: 2021-02-21 15:43
  12. **/
  13. @Slf4j
  14. @RestController
  15. @RequestMapping("user")
  16. public class UserController {
  17. /**
  18. * 表单
  19. * @param user
  20. * @return
  21. */
  22. @PostMapping("save")
  23. public String save(User user) {
  24. log.info("name={}, age={}", user.getName(), user.getAge());
  25. return JSON.toJSONString(user);
  26. }
  27. /**
  28. * json数据格式请求体
  29. * @param user
  30. * @return
  31. */
  32. @PostMapping("json")
  33. public String saveJson(@RequestBody User user) {
  34. log.info("user={}", user.toString());
  35. return JSON.toJSONString(user);
  36. }
  37. }

application.properties 配置文件为空,运行启动类后默认端口号8080。

Postman 模拟表单数据请求及响应效果如下:

后端程序控制台日志打印如下:

Postman 模拟 json 数据请求及响应效果如下:

后端程序控制台日志打印如下:

以上两个情况,请求参数中隐藏的 xss 攻击代码被过滤器过滤后再进入 Contrlloer 层处理。

 

11. 能不能根本上解决问题,即浏览器自动禁止外部注入恶意脚本?

开启 CSP (内容安全策略 Content Security Policy)方法:设置 HTTP 的 头部字段

resp.setHeader("Content-Security-Policy","default-src http: https:");

设置网页的<meta>标签

<meta http-equiv="Content-Security-Policy" content="form-action 'self';">

CSP 常见可选策略设置如下:

策略

含义

default-src http: https: ;

只能通过外联的方式引用 js 和 css

default-src 'self' http://smart4j.cn/;

只能在指定的域下加载文件(包含 img)

form-action 'self'';

form 表单的只能在指定域提交

script-src 'self';

只限制 js 文件在同域加载文件

report-uri /report;

向指定uri发送违规报告(不支持 meta 方式)

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/150556
推荐阅读
相关标签
  

闽ICP备14008679号