XSS(cross-site scripting) 跨站脚本攻击

为了和 CSS 区分,这里把攻击的第一个字母改成了 X

示例

  1. 属性值拼接
const value = "><script>console.log('xss');</script>"
// 会执行console 不能直接复制到浏览器测试  会报错,要用输入的方式
<input type="text" value="value">
<input type="text" value=""><script>console.log('xss');</script>">
  1. css攻击
/* 如果想用 @import  js 代码攻击  根据 w3c 规范 可以发起请求获取 js文件 但是问价不会执行 */
input[type=password][value='1'] { background-color: red; background-image: url(xxx.com);}
input[type=password]:valid { background-color: pink;}
/* 经过多个css选择器匹配 就大概知道密码组合 */

/* 这里会根据上面匹配到的 value=1 然后就会去请求 xxx.com ; valid 和 pattern 是动态的匹配 输入框内容 */
<input type="password" placeholder="input" value="1" pattern="^a">

攻击分类

DOM型

构造恶意的 url -> 用户打开 url -> 浏览器收到响应,开始解析,js 取出 url 中的恶意代码并执行 -> 窃取用户的信息,或者攻击网站接口等

属于前端安全漏洞

存储型

攻击者将代码提交到目标网站的数据库中,像论坛这些,其他用户打开网站后,服务器将代码发送到页面,在浏览器端窃取用户信息发送到第三方

反射型

构造出特殊 url(用户搜索的输入参数) 用户打开这个 url ,服务器将 url 中的代码拼接在html中返回,以此来获取用户信息

与存储型的区别在于,反射型是恶意代码存 url 里

常见的注入方法:

  1. HTML 中内嵌的文本中,恶意内容以 script 标签形成注入
  2. 内联的 JavaScript 中,拼接的数据突破了原本的限制(字符串,变量,方法名等)
  3. 标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签
  4. 标签的 href、src 等属性中,包含 javascript: 等可执行代码
  5. onload、onerror、onclick 等事件中,注入不受控制代码
  6. style 属性和标签中,包含类似 background-image:url("javascript:..."); 的代码(新版本浏览器已经可以防范)
  7. style 属性和标签中,包含类似 expression(...) 的 CSS 表达式代码(新版本浏览器已经可以防范)

解决办法

  1. 输入过滤 首先输入过滤并非十分可靠,如果转码,转码后,内容必须要确定使用在何处,否则显示时为转码 有可能出现乱码,比如 escapeHTML 转码的字符如果直接是放入到html 是没有问题的是可以直接显示的,但是放到 js 的变量赋值中 就可能会报错;但是对于明确的输入类型,还是非常有必要的,比如数字 url 电话等校验
// 两个第三方库
// 1. xss https://github.com/leizongmin/js-xss/blob/master/README.zh.md
xss('用户输入值') // 会将有问题的输入转义
// 自定义白名单 其它的属性删除  stripIgnoreTagBody
{
  // whiteList: {}, // 指定白名单,如果要在默认的基础上添加一些其它的标签(如自定义标签)可以用 ,xss.whiteList 拿到默认的,再添加

  // 去掉不在白名单上的标签
  // 双 script 标签被过滤,标签内容留下
  // 常规校验就开这个即可
  stripIgnoreTag: true,

  // 双 script 标签及其内容会被全部过滤掉
  // 单(注意单标签) script 标签会被替换为 [removed]  其它标签不会
  // stripIgnoreTagBody: ['script'],
  // onIgnoreTag (tag, html, options) {
  //     console.log(tag)
  // },
  escapeHtml (html) {
    // 被识别为有风险的标签字符串才会进入该方法,比如不在白名单里面的标签
    // 标签中的内容 也会进入 这里进入检测
    // 有些情况 <script> 这种,默认是转义为  &lt;script&gt; 所以采用下面方式复写
    return html.replace(/</g, '').replace(/>/g, '')
  }
}


// 2. DOMPurify  https://github.com/cure53/DOMPurify 返回一个去除恶意代码后的字符串
DOMPurify.sanitize('<img src=x onerror=alert(1)//>') // 返回 <img src="x">
  1. 插入html时采用 .innerText .setAttribute .style 等明确方法 使用 .innerHTML、.outerHTML、document.write() 这种较为危险的方式,注意检测

  2. 注意以下情况中出现的恶意代码

eval('恶意代码')
<a href='恶意代码'>xx</a>
<img onclick='恶意代码' onerror='恶意代码' src='恶意代码'>
setTimeout('恶意代码')
setInterval('恶意代码')
location.href = '恶意代码'
</script>
  1. 严格的 CPS 策略 参加下方

  2. 输入内容长度的控制,根据业务限制,控制输入内容的长度,可一定程度上限制xss攻击

  3. cookit 设置 Secure; HttpOnly 防止窃取 关键的 cookie 数据

  4. 验证码

  5. 尽量不要使用 onclick="data" 这种形式的数据绑定 容易被拼接

  6. 拼接 HTML 插入,要注意

  7. 插入属性和链接时 注意拼接问题

vue 中措施

引用官方原话:“第一原则 永远不要使用不可信任的模板”

new Vue({
  el: '#app',
  template: `<div>` + userProvidedString + `</div>` // 永远不要这样做
})

框架已经做了的处理

/* HTML */
<h1>{{ userProvidedString }}</h1>
// 如果 userProvidedString 等于 '<script>alert("hi")</script>'
// 会被转义为 &lt;script&gt;alert(&quot;hi&quot;)&lt;/script&gt;
// 这些转义是通过浏览器原生API 只要浏览器没有漏洞就不存在风险

/* 属性 */
<h1 v-bind:title="userProvidedString">hello</h1>
// 如果 userProvidedString 等于 '" onclick="alert(\'hi\')'
// 会被转义为 &quot; onclick=&quot;alert('hi')

需要自主解决

// 1.
<a v-bind:href="userProvidedUrl">click me</a>
// 这里 userProvidedUrl  是需要过滤的 否则还是可能 被注入 javascript 脚本

// 2.
<a v-bind:style="style">click me</a>
<style>{{ style }}</style>
// 避免用户直接渲染样式,例:登录页覆盖提交按钮,跳转到钓鱼登录页 ,骗取用户登录名和账号

// 3.
<script></script> 
// 元素, 不允许直接渲染

CPS

指令配置:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP

Content-Security-Policy-Report-Only 允许 web 开发人员通过监测 (但不强制执行) 政策的影响来尝试政策

两种使用方式

  1. 前端直接使用
<!-- 一个网站所有内容均来自站点的同一个源 (不包括其子域名) -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self';">
  1. 后端服务配置
# Nginx 配置示例
add_header  Content-Security-Policy  "default-src 'self'";
Last Updated:
Contributors: Warren, Warren