# 四.前端安全

# 1.ajax

Ajax 是一种用于创建快速动态网页的技术。Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。

传统的网页(不使用 Ajax)如果需要更新内容,必须重载整个网页页面

  • AJAX 的工作原理:

    • 0 (未初始化)还没有调用 send()方法
    • 1 (载入)已调用 send()方法,正在发送请求
    • 2 (载入完成)send()方法执行完毕
    • 3 (交互)正在解析相应的内容
    • 4 (完成)响应内容解析完成,可以在客户端调用了
  • 优点:

    • 1.减轻服务器的负担,按需取数据,最大程度的减少冗余请求
    • 2.局部刷新页面,减少用户心理和实际的等待时间,带来更好的用户体验
    • 3.基于 xml 标准化,并被广泛支持,不需安装插件等,进一步促进页面和数据的分离
  • 劣势:

    • 1.AJAX 大量的使用了 javascript 和 ajax 引擎,这些取决于浏览器的支持.在编写的时候考虑对浏览器的兼容性.
    • 2.AJAX 只是局部刷新,所以页面的后退按钮是没有用的.
    • 3.对流媒体还有移动设备的支持不是太好等
      • 怎么解决呢:通过 location.hash 值来解决 ajax 过程中导致的浏览器前进后退按钮失效问题
      • 怎么解决以前被人们常遇到的重复加载问题。主要比较前后的 hash 值,看其是否相等,再判断是否触发 ajax
    let getDate = (method, url, content) = >
    new Promise((resolve, reject) = > {
      let xhr = new XMLHttpRequest()
      xhr.open(method, url)
      xhr.send(content)
      xhr.onreadystatechange = () = > {
        if (xhr.readyState === 4 && xhr.status === 200) {
          resolve(xhr.response)
        }
      }
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 2.跨域

  • 跨域的十种方式
    • 1.降级浏览器
    • 2.jsonp
    • 3.cors
    • 4.node 中间件:proxy-middlewire
    • 5.nginx:配置代理服务将后端请转发给前端
    • 6.websockit:是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是 server push 技术的一种很好的实现。
    • 7.postmessage:h5 跨域方法 frame.contentWindow.postMessage,e.data;e.data,e.source.postMesage
    • 8.localhost.hash:c 页面动态创建 b 页面,请求里面有参数,b 页面 window.parent.parent.location.hash=location.hash,a 通过 location.hash 拿到参数
    • 9.document.name:proxy 一个空的代理页面,被访问的页面动态切换 iframe 页面域名,name 没有消失,frame.contentWindow.name,window.name
    • 10.document.domain:主域名相同,docuemnt.domain 强制设置主域,freame.contentWindow,window.parent

什么是跨域?为什么浏览器要使用同源策略?你有几种方式可以解决跨域问题?了解预检请求吗?

因为浏览器出于安全考虑,有同源策略。也就是说,如果协议,域名或者端口有一个不同就是跨域,Ajax 请求会失败。

**那么是出于什么安全考虑才会引入这种机制呢?**其实主要是用来防止 CSRF 攻击的。简单点说,CSRF 攻击是利用用户的登录态发起恶意请求。

也就是说,没有同源策略情况下,A 网站可以被任意其他来源的 Ajax 访问到内容。如果你当前 A 网站还存在登录态,那么对方就可以通过 Ajax 获得你的任何信息。当然跨域并不能完全阻止 CSRF。

**然后我们来考虑一个问题,请求跨域了,那么情况到底发出去没有?**请求必然是发出去了,但是浏览器拦截了响应。你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不会。因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器任务这不安全,所以拦截了响应。但是表单并不会获取新内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。

# JSONP

JSONP 的原理很简单,就是利用<script>标签没有跨域限制的漏洞。通过<script>标签指向一个需要访问的地址并提供一个回调函数来接受数据。

<script src="http://domain/api?param1=a&param2=b&callback=jsonp"></script>
<script>
  function jsonp(data) {
    conosle.log(data)
  }
</script>
1
2
3
4
5
6

JSONP 使用简单且兼容性不错,但是只限于get请求

在开发总可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就需要自己封装一个 JSONP,以下是简单实现

function jsonp(url, jsonpCallback, success) {
  let script = document.createElement("script")
  script.src = url
  script.async = true
  script.type = "text/javascript"
  window[jsonpCallback] = function (data) {
    success && success(data)
  }
  document.body.appendChild(script)
}
jsonp("http://xxx", "callback", function (value) {
  console.log(value)
})
1
2
3
4
5
6
7
8
9
10
11
12
13

# CORS

CORS 需要浏览器和后端同时支持。IE8 和 9 需要通过XDomainRequest来实现。

浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。

服务端设置Access-Control-Allow-Origin就可以开启 CORS。该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

虽然设置 CORS 和前端没有什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求和复杂请求

# 简单请求

以 Ajax 为例,当满足一下条件是,会触发简单请求

  • 1.使用以下方法之一:GET、HEAD、POST
  • 2.Content-Type的值仅限于下列三者之一:text/plain、multipart/form-data、application/x-www-form-urlencoded

请求中的任意XMLHttpRequestUpload对象均没有注册任何事件监听器,XMLHttpRequestUplaod对象可以使用XMLHttpRequest.upload属性访问。

# 复杂请求

那么狠显然,不符合以上条件的请求肯定是复杂请求了。

对于复杂请求来说,首先会发起一个预检请求,该请求时option方法的,通过该请求来知道服务端是否运行跨域请求。

对于预检请求来说,如果你使用 Node 来设置 CORS 的话,可能会遇到一个这么一个坑。

以下以 express 框架举例:

app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*")
  res.header("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS")
  res.header(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept, Authorization, Access-Control-Allow-Credentials"
  )
  next()
})
1
2
3
4
5
6
7
8
9

该请求会验证你的Authorization字段,没有的话就会报错。

当前端发起了复杂请求后,你会发现就算你代码是正确的,返回结果也永远是报错的。因为预检请求也会进入回调中,也会触发next方法,因为预检请求并不包含Authorization字段,所以服务端会报错。

想解决的这个问题很简单,只需要在回调中过滤option方法即可

res.statusCode = 204
res.setHeader("Content-Length", "0")
res.end()
1
2
3

# docuemnt.domain

该方法只能用于二级域名相同的情况下,比如a.test.comb.test.com适用于该方式。

只需要给页面添加document.domain = 'test.com'表示二级域名都相同就可以实现跨域

# postMessage

这种通常用于获取嵌入页面中的第三方页面数据。一个页面发送消息,另一个页面判断来源并接收消息

// 发送消息
window.parent.postMessage("message", "http//test.com")
// 接收消息端
var mc = new MessageChannel()
mc.addEventListener("message", (event) => {
  var origin = event.origin || event.originalEvent.origin
  if (origin === "http://test.com") {
    console.log("验证通过")
  }
})
1
2
3
4
5
6
7
8
9
10

# 3.XSS

什么是 XSS 攻击?如何防范 XSS 攻击?什么是 CSP?

  • XSS 攻击有哪些类型
    • 存储型
    • 反射型
    • DOM 型
  • 如何防御 XSS 攻击
    • 输入检查
    • 设置 httpOnly
    • 开启 CSP

转义字符 首先,对于用户的输入应该是永远不信任的。最普通的做法就是转义输入输出的内容,对于引号、尖括号、斜杠进行转义。

但是对于显示富文本来说,显然不能通过上面的办法来转义所有的字符,因为这样会把需要的格式也过滤掉。对于这种情况,通常采用白名单过滤的办法,当然也可以通过黑名单过滤,但是考虑到需要过滤的标签和标签属性实在太多,更加推荐白名单的方式。

CSP CSP 本质就是建立白名单,开发者明确告诉浏览器哪些外部资源可以加载和执行。我们只需要配置规则,如何拦截是由浏览器自己实现的。我们可以通过这种方式来尽量减少 XSS 攻击。

1.设置 HTTP Header 中的Content-Security-Policy

2.设置meta标签的方式<meta http-equiv="Content-Securiry-Policy">

这里以设置 HTTP Header 来举例

  • 只允许加载本站资源
Content-Security-Policy:default-src 'self'
1
  • 只允许加载 HTTPS 协议图片
Content-Security-Policy:img-src https://*
1
  • 允许加载任何来源框架
Content-Security-Policy:child-src 'none'
1

对于这种方式来说,只要开发者配置了正确的规则,那么即使网站存在漏洞,攻击者也不能执行它的攻击代码,并且 CSP 的兼容性也不错。

# 4.CSRF

CSRF 中文名为跨站请求伪造。原理即使攻击者构造出一个后端请求地址,诱导用户点击或者通过某些途径自动发起请求。如果用户是在登录状态下的话,后端就以为是用户在操作,从而进行相应的逻辑。

完成 CSRF 需要两个步骤:

  • 1.登陆受信任的网站 A,在本地生成 COOKIE
  • 2.在不登出 A 的情况下,或者本地 COOKIE 没有过期的情况下,访问危险网站 B。

举个例子,假如网站中有一个通过GET请求提交用户评论的接口,那么攻击者就可以在钓鱼网站中加入一个图片,图片的地址就是评论接口。

<img src="http://www.domai.com/xxx?comment='attack'">
1

那么你也是否会想到使用POST方式提交请求是不是就没有这个问题了呢?其实并不是,使用这种方式也不是百分百安全,攻击者同样可以诱导用户进入某个页面,在页面个中通过表单提交POST请求。

如何防御

  • 1、Get 请求不对数据进行修改
  • 2、不让第三方网站访问到用户 Cookie
  • 3、阻止第三方网站请求接口
  • 4、请求时附带验证信息,比如验码或者 Token

SameSite

可以对 Cookie 设置SameSite属性。该属性表示 Cookie 不随着跨域请求发送,可以很大程度减少 CSRF 的攻击,但是该属性目前并不是所有浏览器都兼容。

验证 Referer

对于需要防范 CSRF 的请求,我们可以通过验证 Referer 来判断该请求是否为第三方网站发起的

Token

服务器下发一个随机 Token,每次发起请求时将 Token 携带上,服务器验证 Token 是否有效。

# 5.点击劫持

点击劫持是一种视觉欺骗的攻击手段。攻击者将需要攻击的网站通过iframe嵌套的方式嵌入自己的网页中,并将iframe设置为透明,在页面中透出一个按钮诱导用户点击。

对于这种攻击方式,推荐防御的方法有两种。 X-FRAME-OPTIONS

X-FRAME-OPTIONS是一个 HTTP 响应头,在现代浏览器有一个很好的支持。这个 HTTP 响应头就是为了防御用iframe嵌套的点击劫持攻击。

该响应头有三个值可选,分别是

  • DENY,表示页面不允许通过iframe的方式展示
  • SAMEOPIGIN,表示页面可以在相同域名下通过iframe的方式展示
  • ALLOW-FROM,表示页面可以在指定来源的iframe中展示

# 6.中间人攻击

中间人攻击是攻击方同时与服务端和客户端建立起了连接,并让对方认为连接是安全的,但是实际上整个通信过程都被攻击者控制了。攻击者不仅能获得双方的通信信息,还能修改通信信息。

通常来说不建议使用公共 Wi-Fi,因为很可能就会发生中间人攻击的情况。如果你在通信的过程中涉及到了某些敏感信息,就完全暴露给攻击方了。

当然防御中间人攻击其实并不难,只需要增加一个安全通道来传输信息。HTTPS 就可以用来防御中间人攻击,但是并不是说使用了 HTTPS 就可以高枕无忧了,因为如果你没有完全关闭 HTTP 访问的话,攻击方可以通过某些方式将 HTTPS 降级为 HTTP 从而实现中间人攻击。

上次更新: 2023/1/7 下午3:34:52