[深入08] 前端安全

导航 [深入08] 前端安全
文章图片

[[深入01] 执行上下文](https://juejin.im/post/684490...
[[深入02] 原型链](https://juejin.im/post/684490...
[[深入03] 继承](https://juejin.im/post/684490...
[[深入04] 事件循环](https://juejin.im/post/684490...
[[深入05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490...
[[深入06] 隐式转换 和 运算符](https://juejin.im/post/684490...
[[深入07] 浏览器缓存机制(http缓存机制)](https://juejin.im/post/684490...
[[深入08] 前端安全](https://juejin.im/post/684490...
[[深入09] 深浅拷贝](https://juejin.im/post/684490...
[[深入10] Debounce Throttle](https://juejin.im/post/684490...
[[深入11] 前端路由](https://juejin.im/post/684490...
[[深入12] 前端模块化](https://juejin.im/post/684490...
[[深入13] 观察者模式 发布订阅模式 双向数据绑定](https://juejin.im/post/684490...
[[深入14] canvas](https://juejin.im/post/684490...
[[深入15] webSocket](https://juejin.im/post/684490...
[[深入16] webpack](https://juejin.im/post/684490...
[[深入17] http 和 https](https://juejin.im/post/684490...
[[深入18] CSS-interview](https://juejin.im/post/684490...
[[深入19] 手写Promise](https://juejin.im/post/684490...
[[深入20] 手写函数](https://juejin.im/post/684490...
【[深入08] 前端安全】[[react] Hooks](https://juejin.im/post/684490...
[[部署01] Nginx](https://juejin.im/post/684490...
[[部署02] Docker 部署vue项目](https://juejin.im/post/684490...
[[部署03] gitlab-CI](https://juejin.im/post/684490...
[[源码-webpack01-前置知识] AST抽象语法树](https://juejin.im/post/684490...
[[源码-webpack02-前置知识] Tapable](https://juejin.im/post/684490...
[[源码-webpack03] 手写webpack - compiler简单编译流程](https://juejin.im/post/684490...
[[源码] Redux React-Redux01](https://juejin.im/post/684490...
[[源码] axios ](https://juejin.im/post/684490...
[[源码] vuex ](https://juejin.im/post/684490...
[[源码-vue01] data响应式 和 初始化渲染 ](https://juejin.im/post/684490...
[[源码-vue02] computed 响应式 - 初始化,访问,更新过程 ](https://juejin.im/post/684490...
[深入08] 前端安全
文章图片

XSS

  • XSS ( Cross Site Script ) 跨站脚本攻击
  • Cross Site Script 原本是缩写为CSS,为了区分层叠样式表css,改写为XSS
  • 原理:XSS攻击是指攻击者在网站上注入恶意的客户端代码,通过 恶意脚本 对客服端网页进行篡改,从而在用户浏览网页时,对用户浏览器就行控制,或者获取用户隐私数据一种攻击方式
  • 恶意脚本:主要指 javascrip代码,有时也指 htmlflash
  • 攻击方式:有多种,共同的特征是:窃取用户的隐私数据
  • 攻击类型:可以分为三类,反射型(非持久型)储存型(持久型)基于DOM
  • 危害:
    • 利用虚假的输入表单,骗取用户的个人信息
    • 利用脚本获取用户的cookie
    • 显示伪造的图片和文章
反射型(非持久型)XSS攻击
  • 反射型 XSS 只是简单地把用户输入的数据 “反射” 给浏览器,这种攻击方式往往需要攻击者诱使用户点击一个恶意链接,或者提交一个表单,或者进入一个恶意网站时,注入脚本进入被攻击者的网站。
储存型(持久型)XSS攻击
  • 存储型 XSS 会把用户输入的数据 "存储" 在服务器端,当浏览器请求数据时,脚本从服务器上传回并执行。这种 XSS 攻击具有很强的稳定性。
基于DOM的XSS攻击
  • 基于 DOM 的 XSS 攻击是指通过恶意脚本修改页面的 DOM 结构,是纯粹发生在客户端的攻击。
防御XSS攻击
  • 设置 httpOnly 阻止通过script脚本获取cookie
    • Document.cookie属性XMLHttpRequest对象Request API
    • XMLHttpRequest获取cookie
      • 通过xhr.getResponseHeader('Set-Cookie') // null,不能获取cookie
      • 通过xhr.getAllResponseHeader()获取所有的simple response header,并不包括Set-Cookie字段
      • 注意:这两种方法都只能获取simple response heade,而不能获取Set-Cookie字段
      • 下面会详解介绍XMLHttpRequest
  • 过滤检查:
    • 对input,textares,form表单等做特殊符号的过滤检查
    • HtmlEncode:某些情况下,不能对用户数据进行严格过滤,需要对标签进行转换
    • JavaScriptEncode
    (1) HtmlEncode:对html标签进行转换 < -------------------------- < > -------------------------- > & -------------------------- & '' ------------------------- " 空格 -----------------------  (2) JavascriptEncode:对js一些特殊符号进行转码 " ------------------------ \" \n ----------------------- \\n \r ----------------------- \\r

CSRF
  • CSRF( Cross Site Request Forgery ) 跨站请求伪造 forgeries:伪造品的意思
  • CSRF是一种劫持受信任用户向服务器发送非预期请求的攻击方式
  • 原理:
    • 主要是通过获取用户在目标网站的cookie,骗取目标网站的服务器的信任,在用户已经登录目标站的前提下,访问到了攻击者的钓鱼网站,攻击者直接通过 url 调用目标站的接口,伪造用户的行为进行攻击,通常这个行为用户是不知情的。
    • 即获取了cookie,就可以做很多事情:比如以你的名义发送邮件、发信息、盗取账号、购买商品、虚拟货币转账等等
    案例:CSRF攻击的思想:(核心2和3) 1、用户浏览并登录信任网站(如:淘宝) 2、登录成功后在浏览器产生信息存储(如:cookie) 3、用户在没有登出淘宝的情况下,访问危险网站 // 注意:如果该cookie在没有设置过期时间或者为null,默认是会话时间session-cookie,关闭浏览器后cookie会被清除 // Expires,Max-Age可以设置cookie的过期时间 // 所以这里强调了是没有登出的情况,就有cookie被获取的风险 // 如果cookie设置了具体的过期时间,有效期内都可能被获取 4、危险网站中存在恶意代码,代码为发送一个恶意请求(如:购买商品/余额转账) // 该请求,携带刚刚在浏览器产生的信息(cookie),进行恶意请求 5、淘宝验证请求为合法请求(区分不出是否是该用户发送) // 用HTTP中的header头中的 Refer 来预防 // refer 可以检查请求源,只有合法的请求来源服务器才予以响应 6、达到了恶意目标

预防CSRF攻击
  • 验证码:被认为是对抗CSRF攻击最简洁有效的防御方法
    • CSRF往往是在用户不知情的情况下构建了网络请求,而验证码会强制用户必须与应用进行交互才能完成最终的请求
    • 注意:出于用户考虑,不能给网站的所有操作都加上验证码,所以验证码只能作为防御CSRF的辅助手段
  • Referer检查
    • HTTP中有Refer字段,表示请求来源地址,通过Refer可以检查请求是否来自合法的源,服务器只对合法的源予以响应
    • 2021/4/7更正 => referer检查,而不是refer,注意单词拼写
    • 2021/6/21更正 => refererReferer
  • token
    • CSRF主要就是获取cookie,所以要防御的话,就需要在请求中加入攻击者不能伪造的信息,并且该信息不能保存在cookie中
    • 可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。
复习 cookie
  • cookie是服务器保存在浏览器的一小段文本信息,大小不超过4KB,每次请求都会携带cookie
  • cookie主要用来分辨两个请求知否来自同一个浏览器,和存储一个状态信息:如用户偏好颜色,字体大小等
  • 浏览器可以设置不接受cookie,也可以设置不向浏览器发送cookie
  • window.navigator.cookieEnabled 返回一个布尔值,表示浏览器是否打开cookie功能
  • document.cookie 返回当前网页的cookie
  • 单个域名设置的cookie不应该超过30个,大小不超过 4KB
  • 共享cookie的条件:域名和端口必须一样,不要求协议相同
  • cookie的产生:cookie由HTTP协议生成,也主要是供HTTP协议使用
HTTP回应:cookie的生成
  • 生成cookie:服务器通过设置HTTP回应头信息中,Set-Cookie 字段来保存cookie
  • HTTP回应头信息中,可以包含多个 Set-Cookie 字段
  • 除了 cookie 的值,Set-Cookie 还可以附加cookie的属性,可以包含多个属性,没有次序要求
HTTP/1.0 200 OK Content-type: text/html Set-Cookie: yummy_cookie=choco Set-Cookie: tasty_cookie=strawberry Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly // Set-Cookie: =; Domain=; Secure; HttpOnly多个属性,没有次序要求[page content]

服务器如何修改已经存在的cookie
  • 必须满足四个条件:cookie的 key,domain,path,secure 都匹配
  • 只要有一个属性不同,都会生成全新的cookie
    Set-Cookie: key1=value1; domain=example.com; path=/blog Set-Cookie: key1=value2; domain=example.com; path=/// 上面因为path不一样,就设置了一个全新的cookie// 下次发起请求时,会同时发送两个cookie // 发起请求时,发送的cookie => Cookie: key1=value1; key1=value2 // 注意:两个cookie同名,但是越精确的cookie排在越前面

删除一个现存cookie的唯一方法
  • 设置Expires属性为一个过期的时间
HTTP请求:cookie的发送
  • 浏览器在向服务器发送HTTP请求时,每个请求都会携带上cookie
    • (即浏览器把之前服务器保存在浏览器上的cookie再发回服务器)
    • 发送时使用 Cookie 字段
    • 生成时使用 Set-Cookie 字段
  • Cookie字段可以包含多个cookie,使用 ; 号分割
    GET /sample_page.html HTTP/1.1 Host: www.example.org Cookie: yummy_cookie=choco; tasty_cookie=strawberry

服务器收到浏览器发来的cookie时,有两点无法知道
  • Cookie的各种属性:Expires,Max-Age,Domain,Path,Secure,HttpOnly
  • 哪个域名设置的cookie,一级域名设置的,还是二级域名设置的
Cookie的属性 Expires,Max-Age
  • Expires属性:指定一个具体的过期时间,UTC格式
    • 可以使用 Date.prototype.toUTCString() 进行格式转换
    • 如果不设置Expires属性,或者设置null,该cookie就是session-cookie,只在会话时间内有效,即关闭浏览器窗口,当前session结束,cookie就会被删除
  • Max-Age属性:从现在开始cookie存在的秒数。60*60*24*365一年
  • 同时存在Expires和Max-Age时,Max-Age优先
Domain,Path
  • Domain:指定浏览器发出HTTP请求时,哪些域名要附带这个Cookie
    • 如果没有指定该属性,将其设置为URL的一级域名
  • Path:指定浏览器发出HTTP请求时,哪些路径要附带这个cookie
    • 只要浏览器发现,Path属性是HTTP请求路径的开头一部分,就会在头信息中携带这个cookie
Secure,HttpOnly
  • Secure:指定只有在加密协议HTTPS下,才能将这个cookie发送到服务器
  • HttpOnly:指定该cookie,无法通过脚本拿到
    • Document.cookie属性,XMLHttpRequest对象, Request API 都拿不到该cookie
    • Document.cookie读写cookie
      • Document.cookie读取:读取全部
      • Document.cookie写入:一次只能写入一个,写入不是覆盖,而是添加
      • Document.cookie的读写差异与HTTPT通信格式有关:
      • 发送:HTTP请求发送cookie,Cookie字段,一次可以设置多个cookie,用分号隔开
      • 生成:Http响应生成cookie,Set-Cookie字段,一次设置一个cookie,但可以有多个Set-Cookie字段
XMLHttpRequest
  • const api = new XMLHttpRequest()
  • api.open(method, url, async)
  • api.send(body)
  • api.setRequestHeader()
  • api.getResponseHeader() // 获取参数对应的 simple response header,参数必须在simple response header范围内
  • api.getAllResponseHeader() // 获取所有 simple response header
  • api.onreadystatechange()
  • api.onload()
  • api.onprogress() // 下载进度
  • api.upload.onprogress() // 上传进度信息
  • api.setRequestHeader(name, value)
  • api.responseType // text, document, json, blob, arrayBuffer
  • api.timeout
  • api.abort() // 终止请求
  • api.withCredentidals // 一个布尔值,表示跨域请求时,是否可以携带认证信息,如cookie
    const api = new XMLHttpRequest()

  • 初始化HTTP请求参数(url,http方法等),但并不发送请求,供 send() 方法使用
    api.open(method, url, async, username, password)
    method:HTTP请求的方法GET, POST, HEAD
    url:请求的地址
    async:是否异步,默认是true,即异步的发送请求
    // false:同步,对send方法的调用将阻塞,直到响应完全接受
    // true或者省略:异步,且通常需要调用 onreadystatechange() 方法
    (2) api.send()
  • 发送一个http请求,请求参数写在send方法中
    api.send(body)
    get请求: 参数可以写在open()方法中
    post请求:参数卸载send()方法中
    (3) api.setRequestHeader()
  • 指定一个HTTP请求的头部(请求头),只有在readyState为 1 时才能调用
    api.setRequestHeader(name, value)
    name: key
    value:value
    注意:setRequestHeader()方法可以多次调用,最终的值不是覆盖override而是追加append
    注意:setRequestHeader()方法只有在 readyState = 1 时才能调用,即open()之后send()之前
    (4) api.getResponseHeader()
  • 返回指定的HTTP响应头部的值(响应头)
    (5) api.abort() //abort:中止的意思
  • 取消当前响应,关闭连接并且结束任何未决的网络活动
  • api.abort()将readyState重置为0
  • 应用:如果请求用了太长的时间,而且响应不在必要时,可以调用这个方法
    (6) api.onreadystatechange()
  • api.onreadystatechange()但 readyState = 3 时可以调用多次
  • 注意:onreadystatechange都是小写,而readyState是驼峰写法
    readyState状态:
  • UNSENT ------------------ xhr对象成功构造,open()方法未被调用
  • OPEND ------------------- open()方法被调用,send()方法还未被调用,setRequestHeader()可以被调用
  • HEADERS_RECEIVED -------- send()方法被调用,响应头和响应状态已经返回
  • LOADING ----------------- 响应体(response entity body)正在下载中,此状态下api.response可能已经有了响应数据
  • DONE -------------------- 整个数据传输过程结束,不管本次请求是成功还是失败
    (7) api.onload()
  • api.onload()请求成功时触发,此时 readyState = 4
  • 请求成功的回调有两个:
    1. readyState===4时的api.onreadystatechange()
    1. api.onload()方法
      api.onload = function () {
      //如果请求成功
      if(api.status == 200){
      //do successCallback
      }
      }
    注意:status===200是有坑的,因为协商缓存返回的状态码是304,请求也是成功的请求,所以下面的判断跟完善
    api.onload = function() {
    if((api.status>=200 && api.status < 300) || api.status === 304) {
    // do successCallback

    }
    }
    (8) api.timeout
  • api.timeout用来设置过期时间
    问题1:请求的开始时间怎么确定?是api.onloadstart事件触发的时候,也就是api.send()调用的时候
    解析:因为api.open()只是创建了链接,当并没有真正传输数据,只有调用api.send()时才真正开始传输
    问题2:什么时候是请求结束?
    解析:api.loadend事件触发时结束
    (9) api.onprogress() 下载进度信息
    (10) api.upload.onprogress = function(e) { 上传进度信息
    if ( e.lengthComputable ) {
    const present = e.loaded / e.total * 100;

    }
    }
    1.
    问题1:如何获取response
    提供三个属性来获取response:( api.response ) 和 ( api.responseText ) 和 ( responseXML )
    api.responseText --- api.responseType='text'、''、不设置时,xhr对象上才有此属性,此时才能调用
    api.response --- responseType为""或"text"时,值为"";responseType为其他值时,值为 null
    2.
    问题2:api.responseType有哪些类型
    api.responseType类型有:text, document, json, blob, arrayBuffer
api.getReponseHeader() 和 api.getAllResponseHeader()
  • api.getResponseHeader()获取参数指定的 simple response header
  • api.getAllResponseHeader() 获取所有 simple resopnse header
  • 注意:这两个方法都不能获取Set-Cookie,无论是同域还是跨域
  • 注意:对于跨域请求,客户端只能获取simple response header 和 Access-Control-Expose-Headers
    • simple response header:
      • Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma
    • Access-Control-Expose-Headers:
      • 只在跨域请求时的响应头中存在,表示服务端允许暴露给客服端的headers
      • 同域请求的响应中不包含该字段
      • expose:是暴露的意思
  • 总结:getAllResponseHeader()只能拿到限制以外的response header, getResponseHeader()的参数也必须是限制以外的参数,否则会Refused to get unsafe header的错误
api.withCredentials
  • api.withCredentials一个布尔值,表示跨域请求时,是否可以携带认证信息,默认是false即不允许携带
    • 比如 cookie
  • 注意:在同域请求时,cookie会在请求头中自动携带,但是在跨域请求时,并没有自动携带,原因是:在CORS标准中做了规定,默认情况下,浏览器在发送跨域请求时,不能发送任何认证信息(credentials)如"cookies"和"HTTP authentication schemes"。除非xhr.withCredentials为true
  • 跨域请求:所以跨域请求时
    • 客户端: api.withCredentials = true 设置为true
    • server端(响应头):Access-Control-Allow-Credentials:true
案例
get请求go() { console.log('1111111111'); const api = new XMLHttpRequest(); api.open('GET',----- 初始http请求参数,请求方式,url, 是否异步 'https://bing.ioliu.cn/v1/rand?type=json', true); api.responseType = 'text'; ------ 文本格式的响应 api.timeout = 5000; ---- 请求过期时间 api.setRequestHeader('Content-type', 'application/json'); ----- 必须在open()后,send()前设置 api.onreadystatechange = function() { ------ readyState改变时触发 if ( api.readyState === 4 && this.status === 200) { ---- this指的是api实例 console.log(JSON.parse(this.responseText)) ------ this.response也能拿到同样的数据 } } // 除了在api.onreadystatechange指指定的会调中判断readyState===4,也可以直接在onload中触发 // 两种方法都可以 // 只判断200状态码不完善,应该判断 2xx 或者 304 则请求成功 api.onload = function() { if ( api.status >= 200 && api.status < 300 || api.status === 304 ) { console.log(JSON.parse(api.responseText), 'onload在请求成功时触发'); } } api.send(); ---- 发送数据 }

面试精简 https://juejin.im/post/684490...
https://juejin.im/post/684490...
https://juejin.im/post/684490...
美团 https://juejin.im/post/684490...
知乎 https://zhuanlan.zhihu.com/p/...
XMLHttpRequest https://segmentfault.com/a/11...
我的简书 https://www.jianshu.com/p/729...

    推荐阅读