# 前端安全、中间人攻击

# 上期回顾

# 防抖和节流

/// 防抖
function debounce(func, time, immediate) {
  var timer, result;
  return function () {
    if (timer) clearTimeout(timer);
    const context = this;
    const args = arguments;
    if (immediate) {
      const callNow = !timeout;
      timer = setTimeout(function () {
        timer = null;
      }, time);
      if (callNow) {
        result = func.apply(context, args);
      }
    } else {
      timer = setTimeout(function () {
        func.apply(context, args);
      }, time);
    }
    return result;
  };
}
/// 节流
function throttle(fun, delay = 1000) {
  let timer;
  return function () {
    if (timer) {
      return;
    }
    const context = this;
    const args = arguments;
    timer = setTimeout(function () {
      timer = null;
      fun.apply(context, args);
    }, delay);
  };
}
/// debounce.dart
import "dart:async";
// 函数防抖
/// [func]: 要执行的方法
/// [delay]: 要延迟的时长
Function debounce(
  Function, func,
  [Duration delay = const Duration(milliseconds: 2000)],
){
  Timer timer;
  Function target=(){
    if(timer?isActive ?? false){
       timer?.cancal();
    }
   timer = Timer(delay, (){
      func?call();
    });
  };
}
/** 用法*/
Text("$_count", style:Theme.of(context).textTheme.display1,),
Center(
  child: RaisedButton.icon(
    icon: Icon(Icons.add),
    label: Text("防抖"),
    onPressed: debounce((){
      if(!mouncted){
        return;
      }
      setState((){
        _count++;
      });
    })
  )
)
/// 节流
/// throttle.dart
Function throttle(
  Function Function() func,
){
  if(func == null){
    return func;
  }
  bool enable = true;
  Function target = (){
    if(enable == true){
      enable = false;
      func().then((_){
        enable = true;
      });
    }
  };
  return target;
}
// 用法
Text("$_count", style:Theme.of(context).textTheme.display1,),
Center(
  child: RaisedButton.icon(
    icon: Icon(Icons.add),
    label: Text("防抖"),
    onPressed: throttle(() async {
      await Future.delayed(Duration(milliseconds: 2000));
      if(!mounted){
        return;
      }
      setState((){
        _count++;
      });
    })
  )
)

# 今日解题

# 1) XSS: 跨站脚步攻击

X=> 能 X 的地方就 X,想尽一切办法将可以执行的代码注入到网页中。

# 存储型(server 端)

  • 场景:
    • 常见于带有用户保存数据的网站功能,比如论坛发帖、商品评论、用户私信等。
  • 攻击步骤:
    • i. 攻击者将恶意代码提交到目标网站的数据库中
    • ii. 用户打开目标网站,服务端将恶意代码从数据库中取出,拼接在 HTML 中返回给浏览器
    • iii. 用户浏览器在收到响应后台解析执行,混在其中的恶意代码同时也被执行
    • iiii. 恶意代码窃取用户数据,并发送到指定攻击者的网站,或者冒充用户行为,调用目标网站上的接口,执行恶意操作

# 反射型(server 端)

与存储型的区别在于:存储型的恶意代码存储在数据库中,反射型的恶意代码在 URL 上

  • 场景:
    • 通过 URL 传递参数的功能,比如网站搜索、跳转等
  • 攻击步骤:
    • i. 攻击者构造出特殊的 URL,其中包含恶意代码
    • ii. 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器
    • iii. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行
    • iv. 恶意代码窃取用户数据并发送到攻击者的网站,或冒充用户行为,调用目标网站接口执行攻击者指定操作

# Dom 型(浏览器端)

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

  • 场景:
    • 通过 URL 传递参数的功能,如网站搜索、跳转等。
  • 攻击步骤:
    • i. 攻击者构造出特殊的 URL,其中包含恶意代码
    • ii. 用户打开带有恶意代码的 URL
    • iii. 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行
    • iv. 恶意代码窃取用户数据并发送到攻击者的网站,或冒充用户行为,调用目标网站接口执行攻击者指定操作

# 预防方案

i. 对数据进行严格的输出编码,如 HTML 元素的编码,JS 编码,CSS 编码,URL 编码

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

    ii. CSP HTTP Header,即 Context-Security-Policy、X-XSS-Protectionn

  • 增加攻击难度,配置 CSP(本质是建立白名单,由浏览器进行拦截)
  • Content-Security-Policy: default-src "self" - 所有内容均来自站点的同源(不包括其子域名)
  • Content-Security-Policy: default-src "self" *.trusted.com-允许内容来自信任域名及其子域名(域名不必须与 CSP 设置所在的域名相同)
  • Content-Security-Policy: default-src https://xxx.com- 该服务器仅允许通过 HTTPS 方式并仅从 xxx.com 域名访问文档

    iii. 输入验证:比如一些常见的数字、URL、电话号码、邮箱地址等做校验判断 iv. 开启浏览器 XSS 防御: HTTP Only cookie,禁止 JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注入后也无法窃取此 Cookie 验证码

# 2)CSRF: 跨站请求伪造

攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求,利用受害者在被攻击网站已获取的注册凭证,绕过后台的用户验证,达到冒充用户被攻击的网站执行某项操作的目的

# 攻击流程举例

    1. 受害者登录 a.com,并保留登录凭证 cookie
    1. 攻击者引诱受害者访问 b.com
    1. b.com 向 a.com 发送了一个请求:a.com/act=xxx 浏览器会默认携带 a.com 的 Cookie
    1. a.com 接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求
    1. a.com 以受害者名义执行了 act=xx
    1. 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让 a.com 执行了自定义操作

# 攻击类型

  • i. Get:如在页面的某个 Img 中发起一个 get 请求
  • ii. POST: 通过自动提交表单到恶意网站
  • iii. 链接型:需要诱导用户点击链接

# 预防方案

CSRF 通常从第三方网站发起,被攻击的网站无法防止攻击发生,只能通过自己的网站针对 CSRF 的防御能力来提升安全性。

  • i. 同源检查:通过 Header 中的 Origin Header、Referer Header 确定,但不同浏览器可能会有不一样的实现,不能完全保证
  • ii. CSRF token 校验,将 CSRF Token 输出到页面中(通常保存在 Session 中),页面提交的请求携带这个 Token,服务端验证 Token 是否正确
  • iii. 双重 cookie 验证
    • 流程
        1. 在用户访问网站页面时,向请求域名注入一个 cookie,内容为随机字符串(例如 crsfcookie=vasdasda)
        1. 在前端向后端发起请求时,取出 cookie,并添加到 URL 的参数中(xxx/api/xxx?csrfcookie=vasdasda)
        1. 后端接口验证 cookie 中的字段与 URL 参数中的字段是否一致,不一致则拒绝
    • 优点
      • 无需使用 Session, 适用面广,易于实施
      • token 储存于客户端中,不会给服务器带来压力
      • 相对于 token,实施成本低,可以在前后端统一拦截校验,而不需要一个个接口和页面添加
    • 缺点
      • Cookie 中增加了额外自动
      • 如果有其他漏洞(例如 XSS),攻击者可以注入 Cookie,那么该防御方式失效
      • 难以做到子域名的隔离
      • 为确保 Cookie 传输安全,采用这种防御方式的最好确保用整站 HTTPS 的方式,如果没切 HTTPS 的使用方式会有风险
  • iv. Samesite Cookie 属性: Google 起草了一份草案来改进 HTTP 协议,那就是为 Set-Cookie 响应头新增 Samesite 属性,它用来标明这个 Cookie 是个"同站 Cookie",同站 cookie 只能作为第一方 cookie,不能作为第三方 cookie,Samesite 有两个属性,Strict 为任何情况下都不可以作为第三方 Cookie,Lax 为可以作为第三方 cookie,但必须是 Get 请求

# 3)iframe:安全

# 说明

  • i. 嵌入第三方 Iframe 会有很多不可控问题,同时当第三方 Iframe 出现问题或被挟持之后,也会诱发安全性问题
  • ii. 点击挟持
    • 攻击者将目标网站通过 iframe 嵌套的方式嵌入自己的网页中,将 iframe 设置为透明,诱导用户点击
  • iii. 禁止自己的 iframe 中的链接外部网站的 js

# 预防方案

  • i. 为 Iframe 设置 sandbox 属性,通过它可以对 iframe 的行为进行各种限制,充分实现"最小权限"原则
  • ii. 服务端设置 X-Frame-Options Hedaer 头,拒绝页面被嵌套,X-Frame-Options 是 HTTP 响应头中告诉浏览器一个页面是否可以嵌入<iframe>
    • eg: X-Frame-Options: SAMEORIGIN
    • SAMEORIGIN: iframe 页面的地址只能为同源域名下的页面
    • ALLOW-FROM: 可以嵌套在指定来源的 iframe 里
    • DENY:当前页面不能被嵌套在 iframe 里
  • iii. 设置 CSP 即 Content-security-policy 请求头
  • iv. 减少 iframe 的使用

# 4)错误的内容推断

# 说明

文件上传类型校验失败后,导致恶意的 js 文件上传后,浏览器 Content-type header 的默认解析为可执行的 js 文件

# 预防方案

设置X-Content-Type-Options: nosniff

# 5)第三方依赖包

减少对第三方依赖包的使用,比如之前的 npm 包:event-stream 被爆出恶意攻击数字货币

# 6)HTTPS

# 说明

黑客可以利用 SSL Stripping 这种攻击手段,强制让 HTTPS 降级到 HTTP,从而继续继续中间人攻击

# 预防方案

使用 HSTS(HTTP Strict Transport Security), 它通过下面这个 HTTP Header 以及一个预加载的清单,来告诉浏览器和网站进行通信的时候强制性使用 HTTPS,而不是通过明文 HTTP 进行通信,这里的强制性表现为浏览器无论在何种情况下都直接向服务端发起 https 请求,而不再像以往那样从 HTTP 跳转到 HTTPS。另外,当遇到证书或链接不安全的时候,首先警告用户,而不再让用户选择是否继续进行不安全通信。

# 7)本地存储数据

避免重要用户信息存在浏览器缓存中

# 8)静态资源完整性交易

# 说明

使用内容分发网络(CDNS)在多个站点之间共享脚本和样式表等文件可以提高网站性能并节省宽带。然而使用 CDN 也存在危险,如果攻击者获得对 CDN 的控制权,则可以将任意恶意内容注入到 CDN 的文件中(或完全替换调文件),因此可能潜在攻击所有从该 CDN 获取文件的站点

# 预防方案

将使用 base64 编码过后的文件哈希值写入你所引用的 script 或标签 integrity 属性值中,即可启用子资源完整性能。

# 9)网络劫持

# 说明

  • DNS 劫持(涉嫌违法): 修改运行商的 DNS 记录,重定向到其他网站。DNS 劫持是违法的行为,目前 DNS 劫持已被监管,现在很少见 DNS 劫持
  • HTTP 劫持:前提是 HTTP 请求,因为 HTTP 是明文传输,运营商可借机修改 HTTP 响应内容,如加广告

# 预防方案

全站用 HTTPS

# 10)中间人攻击(Man-in-the-middle attack, MITM)

指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者窃听、篡改甚至完全控制。没有进行严格的证书校验是中间人攻击的着手点。目前大多数加密协议都提供 一些特殊认证方法以阻止中间人 攻击,如 SSL(安全套接字层)协议可以验证参与通讯的用户证书是否有权威、受信任的数字证书认证机构颁发,并且能执行双向身份认证。攻击 场景如用户在一个未加密的 WiFi 下访问网站,在中间人攻击中,攻击者可能拦截通讯双方的通话并插入新的内容。

# 场景

  • i)在一个未加密的 Wi-Fi 无线接入点的接受范围内的中间人攻击者,可以将自己作为一个中间人插入这个网络
  • ii)Fiddler / Charles (花瓶)代理工具
  • iii)12306 之前的自己证书

# 过程

  • i)客户端发送请求到服务端,请求被中间人截获
  • ii)服务器向客户端发送公钥
  • iii)中间人截获公钥,保留在自己手上。然后自己生成一个【伪造的】公钥,发给客户端
  • iv)客户端收到伪造的公钥后,生成加密 hash 值发给服务器
  • v)中间人获得加密 hash 值,用自己的私钥解密获得真秘钥,同时生成假的加密 hash 值,发给服务器
  • vi)服务器用私钥解密获得假密钥,然后加密数据传输给客户端

# 使用抓包工具 fiddle 来进行举例说明

  • 首先通过一些途径在客户端安装证书
  • 然后客户端发送连接请求,fiddle 在中间截取请求,并返回自己伪造的证书
  • 客户端已经安装了攻击者的根证书,所以验证通过
  • 客户端就会正常和 fiddle 进行通信,把 fiddle 当作正确的服务器
  • 同时 fiddle 会跟原有的服务器进行通信,获取数据以及加密的密钥,去解密密钥

# 常见攻击方式

  • 嗅探:嗅探是一种用来捕获流进和流出的网络数据包的技术,就好像是监听电话一样。比如:抓包工具
  • 数据包注入:在这种,攻击者会将恶意数据包注入到常规数据中的,因为这些恶意数据包是在正常的数据包里面的,用户和系统都很难发现这个内容。
  • 会话劫持:当我们进行一个网站的登录的时候到退出登录这个时候,会产生一个会话,这个会话是攻击者用来攻击的首要目标,因为这个会话,包含了用户大量的数据和私密信息。
  • SSL 剥离:HTTPS 是通过 SSL/TLS 进行加密过的,在 SSL 剥离攻击中,会使 SSL/TLS 连接断开,让受保护的 HTTPS,变成不受 保护的 HTTP(这对于网站非常致命)
  • DNS 欺骗,攻击者往往通过入侵到 DNS 服务器,或者篡改用户本地 hosts 文件,然后去劫持用户发送的请求,然后转发到攻击者想要转发到的服务器
  • ARP 欺骗,ARP(address resolution protocol)地址解析协议,攻击者利用 APR 的漏洞,用当前局域网之间的一台服务器,来冒充客户端想要请求的服务端,向客户端发送自己的 MAC 地址,客户端无从得到真正的主机的 MAC 地址,所以,他会把这个地址当作真正 的主机来进行通信,将 MAC 存入 ARP 缓存表。 代理服务器

# 预防方案:

  • i)用可信的第三方 CA 厂商
  • ii)不下载未知来源的证书,不要去下载一些不安全的文件
  • iii)确认你访问的 URL 是 HTTPS 的,确保网站使用了 SSL,确保禁用一些不安全的 SSL,只开启:TLS1.1,TLS1.2
  • iv)不要使用公用网络发送一些敏感的信息
  • v)不要去点击一些不安全的连接或者恶意链接或邮件信息

# 11)SQL 注入

# 说明

通过把 SQL 命令插入到 web 表单递交或输入域名或页面请求的查询字符串,最终达到欺骗数据库服务器执行恶意 sql 命令,从而达到和服务器进行直接交互

# 预防方案

    1. 后台进行输入验证,对敏感字符过滤
    1. 使用参数化查询,能避免拼接 sql,就不要拼接 sql

# 12)前端数据安全

# 说明

反爬虫,如猫眼电影、天眼查等等,以数据内容为核心资产的企业

# 预防方案

  • font-face 拼接方式
  • background 拼接
  • 伪元素隐藏:汽车之家
  • 元素定位覆盖式:去哪儿
  • iframe 异步加载:网易云音乐

# 13)其他方面

    1. 定期请第三方机构做安全性测试,漏洞扫描
    1. 使用第三方开源库做上线前的安全测试,可以考虑融合到 CI 中
    1. code review 保证代码质量
    1. 默认项目中设置 header 请求头,如x-xss-protection:1; mode=block,x-content-type-options:nosniff,X-frame-options:SAMEORIGIN, content-security-policy:default-src * blob:;...等等
    1. 对第三方包和库做检测:NSP(node security platform, snyk