IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> HTTP网络面经 -> 正文阅读

[网络协议]HTTP网络面经

经典五层模型

请添加图片描述

  • 物理层主要作用是定义物理设备如何传输数据,机器的硬件,网卡端口,网线等。
  • 数据链路层在通信的实体间建立数据链路连接,比如最基础的数据传输数据流,可以自己选择二进制或者ASCII码形式等
  • 网络层在数据在结点之间传输创建逻辑链路,比如输入百度,网络层会为我们找到百度的网址,如何寻找到的过程就是网络层要做的事。
  • 传输层:向用户提供可靠的端到端(End-to-End)服务
    • 传输层向高层屏蔽了下层数据通信的细节
  • 应用层:
    • 为应用软件提供了很多服务
    • 构建于TCP协议之上
    • 屏蔽网络传输相关细节

HTTP

简介

HTTP协议是 Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网服务器传输超文本到本地浏览器的传送协议。HTTP是基于TCP/IP协议通信协议来传递数据(HTML 文件,图片文件,查询结果等)。它不涉及数据包(packet)传输,主要规定了客户端和服务器之间的通信格式,默认使用80端口

特点

  1. 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、PUT、DELETE、POST。每一种方法规定了客户与服务器联系的类型不同。优于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
  2. 灵活:HTTP允许传输任意类型的数据对象
  3. 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  4. 无状态HTTP协议是无状态,HTTP协议自身不对请求和响应之间的通信状态进行保存。任何两次请求之间都没有依赖关系。直观地说,就是每个请求都是独立的,与前面的请求和后面的请求都是没有直接联系的。协议本身并不保留之前一切的请求和响应报文的消息。这是为了更好地处理大量事务,确保协议的可伸缩性,而特意把HTTP协议设计成如此的简单

method(方法)(重点)

以前的 method 方法只有 GetPost 方法

方法有哪一些呢?表示什么含义呢?

  • get 获取数据
  • post 新建数据
  • patch/put 更新数据
  • delete 删除数据

Restful API

  • 一种新的API设计方法(早已推广使用)
  • 传统API设计:把每个url当做一个功能
  • Restful API 设计:把每个 url 当做一个唯一的资源

如何设计成一个资源?

  • 尽量不用url参数
    • 传统的API设计:/api/list?pageIndex=2
    • Restful API设计:/api/list/2
  • 用method表示操作类型
    • 传统的
      • post 请求 /api/create-blog
      • post 请求 /api/update-blog?id=100
      • get 请求 /api/get-blog?id=100
    • Restful
      • post 请求 /api/blog
      • patch 请求 /api/blog/100
      • get 请求 /api/blog/100

注意:

GET 和 POST 的区别?

  • GET在浏览器回退时是无害的,而POST会再次提交请求
  • GET请求会被浏览器主动缓存,而POST不会,除非手动设置
  • GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留
  • GET请求在URL中传送的参数是有长度现在的,而POST没有限制
  • GET参数通过URL传递,POST放在Request body中

状态码

状态码由三位数字组成,第一个数字定义了响应的类别,可以大致分为五类:

分类

  • 1xx:指示信息,表示服务器已收到请求
  • 2xx:请求成功,表示请求已被成功接收
  • 3xx:请求重定向,要完成请求必须进行更进一步的操作
  • 4xx:客户端错误,请求有语法错误或请求无法实现
  • 5xx:服务端错误,服务器未能实现合法的请求

常见状态码

  • 200 成功
  • 301 永久重定向(配合 location,浏览器自动处理)
  • 302 临时重定向(配合 location,浏览器自动处理)
  • 304 资源未被修改,配合缓存使用
  • 403 被请求页面被禁止访问
  • 404 资源不存在
  • 403 没有权限
  • 500 服务端错误
  • 502 网关错误
  • 504 网关超时

关于协议和规范

  • 状态码就是一个约定
  • 要求大家都跟着执行
  • 不要违反规范,例如 IE 浏览器

http headers

常见的 Request Headers

  • 客户端往服务端发的headers
  • Accept 浏览器可接收的数据格式
  • Accept-Encoding 浏览器可接收的压缩算法,如gzip
  • Aceept-Language 浏览器可接收的语言,如zh-CN
  • Connection:keep-alive 一次TCP连接重复使用
  • cookie:
  • Host: 域名
  • User-Agent (简称 UA) 浏览器信息
  • Content-type 发送数据的格式,如 application/json

常见的 Response Headers

  • 服务端往客户端发的headers
  • Content-type 返回数据的格式,如 application/json
  • Content-length 返回数据的大小,多少字节
  • Content-Encoding 返回数据的压缩算法,如 gzip
  • Set-Cookie:修改 cookie

自定义 header

在这里插入图片描述

名字和值都是自定义的

发展

HTTP/0.9

  • 只有一个命令 get
  • 没有HEADER等描述数据的信息
  • 服务器发送完毕后,就关闭TCP连接

HTTP /1.0

  • 增加了很多命令(put,path,等)
  • 增加了status code 和header
  • 多字符集支持、多部分发送、权限、缓存等

HTTP /1.0 版本及其之前的版本是没有持久连接的,结果就是每进行一次HTTP通信就要断开一次TCP连接。这样就会导致每请求一个资源从新建立一次TCP连接,这样无疑会让请求每次会造成无谓的TCP连接建立和断开,增加通信量的开销

HTTP /1.1(都在使用)(重点)

新增部分:

  • 持久连接(所有请求都默认开启了的)
  • pipline (同一个TCP连接发送多个请求)
  • 增加了host和其他一些命令

持久连接:只要任意一端没有明确提出断开连接,则保持TCP连接状态。持久化连接的好处在于减少了TCP连接的重复建立和断开所造成造成的额外开销,减轻了服务器端的负载。另外,减少开销的那部分时间,使HTTP请求和响应能够更早地结束,这样Web页面的显示速度也就相应提高了。

管线化:持久连接使得多数请求以管线化(pipelining)方式发送成为可能。从前发送请求后需等待并收到响应,才能发送下一个请求。管线化技术出现后,不用等待响应也可直接发送下一个请求。这样就能够做到同时并行发送多个请求,而不需要一个接一个地等待响应了。通俗地讲,请求打包一次传输过去,响应打包一次传递回去。管线化的前提是在持久连接下

例如:一个页面中有3个请求,它的工作方式就会是:请求1->请求2->请求3->响应1->响应2->响应3;而以前的工作方式就是:请求1->响应1->请求2->响应2->请求3->响应3,可以细细体会下,管线化一次性把请求发送出去,而以前的工作方式需要请求出去,等响应回来,才能进行下一个请求。高下立判,所以持久连接可以让请求更快结束。而管线化技术则比持久连接还要快

缺点:

  • 高延迟,带来页面加载速度的降低。网络延迟问题主要由于对头阻塞(Head-Of-Line Blocking),导致带宽无法被充分利用。那如何有效的规避这个bug呢?原理我认为其实就是将多个请求合并成一个请求,减少请求头带来的影响
    • Chrome有一个机制,对于同一个域名,默认允许同时建立6个(Firefox 好像是8个)TCP持久连接,使用持久连接时,虽然能公用一个TCP管道,但是在同一个管道中同一时刻只能处理一个请求,在当前的请求没有结束之前,其他的请求只能处于阻塞状态。举个例子:如何现在同一个域名下同时有12个请求发生,那么其中6个请求会进入排队等待状态,直至进行中的请求完成。
    • 雪碧图合并多张小图为大图,再用JavaScript或者CSS将小图重新"切割"出来的技术
    • 内联也是一种防止发送小图片的请求的技巧,将图片的原始数据(base64的形式)直接写到CSS文件里面的UPL里,减少网络的请求次数。
  • 无状态特性,带来的巨大HTTP头部,由于请求头一般会携带"user Agent"、“Cookie”、“Accept”、“Server"等许多固定的头字段,这些加起来多大几百字节甚至上千字节,但是Body体却经常只有几十字节(比如GET请求、204/301/304响应),成了不折不扣的"大头儿子”。Header李携带的内容过大,在一定程度上增加了传输的成本。更重要的是,成千上万的请求响应报文里有很多字段值都是重复的,非常浪费的。
  • 明文传输,带来的不安全性,1.1版本的数据传输都是明文,所有传输的内容都是明文,客户端和服务端都无法验证对方的身份,这在一定程度上无法保证数据的安全性。举一个例子:“免费WiFi陷阱”,有人就利用HTTP明文传输的缺点,把所有流量都会被截获保存,里面如果有银行卡号,网站密码等敏感信息的话就危险了,黑客拿到了这些数据就可以冒充你为所欲为
  • 不支持服务器推送消息

HTTP2(国外用的多)(重点)

HTTP/2基于SPDY,专注于性能,最大的一个目标是在用户和网站间只用一个连接(connection),(公开的)SPDY协议是有谷歌开发的,主要是解决HTTP/1.1效率不高的这个痛点。

新增部分:

HTTP/2传输数据量的大幅减少,主要有两个原因:以二进制方式传输和Header压缩

  • 二进制传输:可以将请求和响应数据分割为更小的帧,并且他们采用二进制编码。用"HEADERS"帧存放头数据、“DATA"帧存放实体数据,以前的"Header+Body"的报文结构就完全消失了,我们看到的就是一个个的"碎片”。在HTTP/2中,同域名下所有通信都在单个连接上完成,每个数据流都以消息的形式发送,而消息又由一个或多个帧组成。多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装
  • Header压缩: HTTP/2的压缩算法是"HPACK"算法,我们可以简单的理解为在多个请求(2个以上)陆续发送时,第一个请求会全部请求头发送过去,之后的请求就会和之前的请求进行比较,看只需要发送不一样的数据就可以了,这样可以减少数据冗余,降低开销
  • 多路复用:在HTTP/2中引入了多路复用的技术,可以很好的解决浏览器限制在同一域名下的请求数量的问题,同时也更容易实习全速传输。
  • Server Push:HTTP/2在一定程度上改变了传统的"请求-应答"工作模式,服务器不再是完全被动地响应请求,也可以主动向客户端发送消息,减少等待的延迟,这称为"服务器推送"(Server Push,也叫 Cache push)。另外服务端可以主动推送,客户端可有权利选择是否接收。如果服务器推送的资源已经被浏览器缓存国,浏览器可以通过发送RST_STREAM帧来接收。主动推送页遵守同源策略,换句话说,服务器不能随便讲第三方资源推送给客户端,而必须是经过双方确认才可以的。
  • 提高安全性:HTTP/2是加密的,是应为使用了’https’协议名

HTTP3(了解)

  • QUIC是HTTP3的底层支撑协议
  • QUIC是基于UDP实现,有吸取TCP中的精华,实现即快有可靠的协议
  • 很好的解决了"对头阻塞"问题

HTTPS(重点)

HTTPS实在HTTP上建立SSL加密层,并对传输数据进行加密,是HTTP协议的安全版。现在的应用是非常广泛的,尤其是涉及数据安全比较敏感的地方

主要的作用:

  1. 数据进行加密(隐秘性)(以前的HTTP报文使用明文(指未经过加密的报文)方式发送),并建立一个信息安全通道,来保证传输过程中的数据安全;
  2. 保证数据的完整性
  3. 对网站服务器进行真实身份认证

如何判断该页面是否用了HTTPS协议呢?

浏览器的地址栏开头是否是HTTPS协议,如果是https://就表示该协议是HTTPS,如果是http://就表示是HTTP协议。另外,还可以查看地址栏左边是否有一个带锁的标记。

HTTP和HTTPS有什么区别吗?其实HTTPS就是在HTTP加上TLS/SSL协议,就是HTTPS=HTTP+TLS/SSL

风险优势
信息窃听信息加密
信息篡改完整性校验
信息劫持身份验证

而TLS/SSL的实现主要依赖与三类基本的算法:非对称加密 + 对称加密 + 散列算法

  • 非对称加密算法:进行身份认证和秘钥协商
  • 对称加密算法:采用协商的秘钥对数据加密
  • 散列函数:基于散列函数验证信息的完整性

在传输过程中还使用了 数字签名 来确保数据不会被篡改,确定消息的完整性。它的原理可以这样理解:

数字签名的生成:将一段文本先用Hash函数生成消息摘要,然后再用发送者的私钥加密生成数字签名,将原文与数字签名一起传输给接收者

校验数字签名: 接收者只有用发送者的公钥才能解密被加密的摘要信息,然后使用HASH函数对收到的原文产生一个摘要信息,与上一步得到的摘要信息对比。如果相同,则说明收到的信息是完整的,在传输过程中没有被修改,否则说明信息被修改过,因此数字签名能够验证信息的完整性。

但是有一个关键的问题,如何将公钥给接收者呢?注意公钥不能在不安全的网络中直接发送给接收者,或者接收者拿到的公钥如何证明是发送者的呢?

所以我们引入了CA(第三方的数字证书认证机构),下面介绍一下大概的流程:

  1. 服务器的运营人员向第三方机构CA提交公钥、组织信息、个人信息等信息应申请认证;
  2. CA通过线上、线下等多种手段验证申请者提供的真实性,如组织是否存在、企业是否合法,是否拥有域名的所有权等;
  3. 如信息审核通过,CA会想申请者签发认证文件-证书。证书包含以下信息:申请者公钥,申请者的组织信息和个人信息、签发机构CA的信息、有效时间、证书序列号等信息的明文,同时包含一个签名。其中签名的产生算法:首先使用散列函数计算公开的明文信息的信息摘要,然后,采用CA的私钥对信息摘要进行加密,密文即签名;
  4. 客户端Client向服务器Server发出请求是,Server返回证书文件;
  5. 客户端Client读取证书中的相关的明文信息,采用相同的散列函数计算得到信息摘要,然后,利用对应CA的公钥解密签名数据,,对比证书的信息再要,如果一致,这可以去人证书的合法性,即服务器的公开秘钥是值得信赖的。
  6. 客户端还会验证证书相关的域名信息、有效时间等信息;客户端会内置信任CA的证书信息(包含公钥),如果CA不被信任,则找不到对应CA的证书,证书也会被判定非法。

HTTPS的工作流程

  1. Client端发起一个HTTPS(比如https://xx.com)的请求.更加RFC2818的规定,Client知道需要连接Server的443(默认)端口。
  2. Server端会事先配置好的公钥证书(public key certificate)返回给客户端。
  3. Client验证公钥证书:比如是否在有效期内,证书的用途是不是匹配Client请求的站点,是不是在CRL吊销列表里面,它的上一级证书是否有效,这是一个递归的过程,知道验证到根证书(操作系统内置的Root证书或者Client内置的Root证书)。如果验证通过则继续,不通过则显示警告信息。
  4. Client使用伪随机数生成器生成加密所使用的对称秘钥,然后用证书的公钥加密这个对称秘钥,发给Server。
  5. Server使用自己的私钥(Private Key)解密这个消息,得到对称秘钥。至此,Client和Server双发都持有了相同的对称秘钥
  6. Server使用对称秘钥加密"明文加密A",发送给Client
  7. Client使用对称秘钥解密响应的密文,得到"明文内容A"
  8. Client再次发起HTTPS请求,使用对称秘钥机密请求的"明文内容B",然后Server使用对称秘钥解密密文,得到"明文内容B"

他们的过程可以总结为:在交换秘钥环节使用非对称加密方式,之后的建立通信交换报文阶段则使用对称加密方式 。具体的做法就是: 发送密文的一方使用对方的公钥进行加密处理"对称的秘钥",然后对方用自己私钥解密拿到"对称的密钥",这样可以确保交换的密钥是安全的前提下,使用对称加密方式进行通信。

HTTP 和 HTTPS 的区别

  • HTTP的明文传输协议,HTTPS协议是由SSL+HTTP协议构建的课进行加密传输、身份认证的网络协议,比HTTP协议更安全
  • HTTPS比HTTP更加安全,对搜索引擎更友好,理由SEO,谷歌、百度优先索引HTTPS网页;
  • HTTPS需要用到SSL证书,而HTTP不用;
  • HTTPS标准端口443,HTTP标准端口80;
  • HTTPS基于传输层,HTTP基于应用层;
  • HTTPS在浏览器显示绿色安全锁,HTTP没有显示

TCP与UDP

它们都处于传输层,都是定义了如何进行数据传输

UDP

特点

UDP协议全称是用户数据包协议,在网络中它与TCP协议一样用于处理数据包,是一种面向无连接、不可靠的协议。处于OSI模型中的传输层。UDP不提供数据包分组、组装和不能对数据包进行排序的缺点。

它有以下几个特点:

  1. 面向无连接

UDP有不提供数据包分组,组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整的到达;UDP是不需要和TCP一样在发送数据前进行三次握手,想发数据就可以发数据。举个例子:

  • 在发送端,应用层将数据传递给传输层的UDP协议,UDP只会给数据增加一个UDP头标识下是UDP协议,然后就传递给网络层了
  • 在接收端,网络层将数据传递给传输层,UDP只去除IP报文头就传递给应用层,不会有任何拼接操作
  1. 单播、多播、广播

UDP不止支持一对一的传输方式,同样支持一对多,多对多,多对一的方式,也就是说UDP提供了单播,多播,广播的功能

  1. 面向报文的

发送方的UDP对应用层传下来的报文,会在首部添加,就向下传给IP层了。UDP对应用层下来的报文,既不合并,也不会拆分,而是保留这些报文的便捷。因此,应用程序必须选择适合大小的报文。

  1. 不可靠性

通信是不需要建立连接,想发就发,并且无论收到什么数据就传递什么数据,并且也不会备份数据,发送数据也不会关心对方是否已经正确接受到数据了。还有就是网络速度时好时坏,但是UDP因为没有拥塞控制,一直会以恒定的速度发送数据,并不会根据环境来调整发送速率,这也会导致在网络条件不好的情况下导致丢包。但是如果是一些实效性要求高的场景(比如直播,在线会议)就需要UDP。

  1. 头部开销小

UDP的头部包含几个数据:

  • 两个十六位的端口号,分别为源端口(可选字段)和目标端口
  • 整个数据报文的长度
  • 整个数据报文的检验和(IPv4可选 字段),该字段用于发现头部信息和数据中的错误

因此UDP的头部开销小,只有八字节,相比TCP的至少二十字节要少的多,在传输数据报文时是很高效的。

TCP(重点)

TCP协议全称是传输控制协议,是一种面向有连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 783定义。TCP是面向连接的、可靠的流协议。流就是指不间断的数据结构,你可以把它想象成排水管中的水流。

特点

  1. 面向连接:面向连接,是指的是发送数据之前必须在两端建立连接。建立连接的方法是"三次握手",这样能建立可靠的连接。建立连接,是为了数据的可靠传输打下了基础。
  2. 仅支持单播传输:每条TCP传输连接只能有两个端点,只能进行点对点的数据传输,不支持多播和广播传输方式。
  3. 面向字节流:TCP不是UDP,不需要一个个报文地进行传输,而是在不保留报文边界的情况下以字节流方式进行传输
  4. 可靠传输:对于可靠传输,判断丢包,误码靠的是TCP的段编号以及去人好。TCP为了保证报文传输的可靠,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的字节发回了一个相应的确认(ACK);如果发送端实体在合理的往返延(RTT)内未收到确认,那么对应的数据(假设丢失了)将会被重传。
  5. 提供拥塞控制:当网络出现拥塞的时候,TCP能够减小向网络注入数据的速率和数量,缓解拥塞
  6. TCP提供全双工通信:TCP允许通信双方的应用程序在任何时候都能发送数据,因为TCP连接的两端都设有缓存,用来临时存放双向通信的数据。当然,TCP可以立即发送一个数据段,也可以缓存一段时间以便一次发送更多的数据段(最大的数据段大小取决于MSS)

三次握手(建立连接的过程)(重点)

  1. 第一次握手:建立连接时,客户端发送SYN包(SYN=j)到服务器,并进入SYN_SENT状态,等待服务器确认
  2. 第二次握手:服务器收到SYN包,必须确认客户的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器进入SYN_RECEIVED状态
  3. 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

完成了三次握手后,客户端与服务器开始传送数据

面试简述的话,可以这样说:

首先A向B发起SYN(同步请求),然后B回复SYN+ACK(同步请求应答),最后A回复ACK确认,这样TCP的一次连接(三次握手)的过程就建立了

四次挥手(断开连接的过程)(重点)

刚开始双方都处于 ESTABLISED 状态,假如是客户端先发起关闭请求的话,这:

  1. 第一次挥手:客户端发送一个FIN报文,报文中会指定一个序列号。此时客户端处于FIN_WAIT_1状态。
  2. 第二次挥手:服务端收到FIN之后,此时服务端处于CLOSE_WAIT状态。会发送ACK报文,且把客户端的序列号值+1作为ACK报文的序列号值,表明已经收到客户端的报文了,此时客户端处于FIN_WAIT_2状态。
  3. 第三次挥手:如果服务也想断开连接了,此时服务端处于LAST_ACK的状态,和客户端的第一个挥手一样,发给FIN报文,且指定一个序列号。
  4. 第四次挥手:客户端收到FIN之后,一样发送一个ACK报文作为应答,且把服务端的序列号+1作为自己ACK报文的序列号值,此时客户端处于TIME_WAIT状态。经过一会儿以确保服务端收到字节的ACK报文之后才会进入CLOSED状态

服务端收到ACK报文之后,就处于关闭连接了,处于CLOSED状态。

TCP和UDP的区别

UDPTCP
是否连接无连接面向连接
是否可靠不可靠传输,不使用流量控制和拥塞控制可靠传输,使用流量控制和拥塞控制
连接对象个数支持一对一,一对多,多对一和多对多交互通信只能是一对一通信
传输方式面向报文面向字节流
首部开销首部开销小,仅8个字节首部最小20字节,最大60字节
适用场景适用于实时应用(IP电话、视频会议、直播等)适用于要求可靠传输的应用,例如文件传输

总结一下:

  • TCP向上层提供面向连接的可靠服务,UDP向上层提过无连接不可靠服务。
  • 虽然UDP并没有TCP传输来的准确,但是也能在很多实时性要求高的地方有所作为
  • 对数据准确性要去高,速度相对较慢的,可以选用TCP

http 缓存机制

介绍

什么是缓存

可以将一些没有必要获取一遍的东西,不在重新获取,放在本地

为什么需要缓存

通过缓存,尽量减少网络请求的数量,加快渲染

哪些资源可以被缓存

  • 静态资源(js css img)
  • 字体图标

缓存的位置(了解)

Service Worker

Service Worker 是允许在浏览器背后的独立线程,一般可以用来实现缓存功能。使用Service Worker传输协议必须为HTTPS。因为Service Worker中涉及到请求拦截,所以必须使用HTTPS协议来保障安全。Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存、并且缓存是持续性的。

Service Worker实现缓存功能一般分为三个步骤:首先需要先注册Service Worker,然后监听到install事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据

当Service Worker没有命中缓存的时候,我们需要去调用fetch函数获取数据。也就是说,如果我们没有在Service Worker命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从Memory Cache中还是从网络请求中获取的数据,浏览器都会显示我们是从Service Worker中获取的内容。

Memory Cache

Memory Cache 就是内存中的缓存,主要包含的是当前页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。读取内存中的数据肯定比磁盘快,内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。一旦我们关闭Tab页面,内存中的缓存也就被释放了

**那么既然内存缓存那么高效,我们是不是让所有数据都存放在内存中呢?**那是不可能的,也是不现实的,因为内存肯定是比硬盘的容量小很多。我们可以使用preloader相关的指令(例如 <Link rel="prefetch">)下载的资源,它可以一遍解析js/css 文件,一般网络请求下一个资源

注意:内存缓存在缓存资源时并不关心返回资源的HTTP缓存头Cache-Control是什么值,同时支援的匹配也并非仅仅是对URL做匹配,还可能会对Content-Type,CORS等其他特征做校验

Disk Cache

存储在硬盘是上的缓存,读取速度慢了点,但是什么都能存储到磁盘中,比之Memory Cache 胜在容量和存储时效性上

在所有浏览器缓存中,Disk Cache覆盖面基本是最大的。它会根据HTTP Header 中的字段判断那些资源需要缓存,那些资源可以不请求直接使用,那些资源已经过期需要重新请求。并且即使在跨站点的情况下,相同地址的协议一旦被硬盘缓存下来,就不会再次去请求数据。绝大部分的缓存都来自Disk Cache,关于HTTP的协议头中的缓存字段,我们会在下文进行详细介绍。

浏览器会把哪些文件丢进内存中?哪些丢进硬盘中?

关于这点,网上说法不一,不过一下观点比较靠得住:

  • 对于大文件来说,大概率是不存储在内存中的,反之优先
  • 当前系统内存使用率高的话,文件优先存储进硬盘

Push Cache

Push Cache(推送缓存)是HTTP/2中的内容,当以上三种缓存都没有命中时,它才会被使用。**它只在回话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,**在Chrome浏览器中只有5分支左右,同时它也并非严格执行HTTP中的缓存指令。

  • 所有的资源都能被推送,并且能够被缓存,但是Edge和Safari浏览器支撑相对比较差
  • 可以退出 no-cache 和 no-store 的资源
  • 一旦连接被关闭,Push Cache就被释放
  • 多个页面可以使用同一个HTTP/2的连接,也就可以使用统一Push Cache。这主要还是依赖浏览器的实现而定,出于对性能的考虑,有的浏览器会对相同域名但不同的tab标签使用同一个HTTP连接。
  • Push Cache中的缓存只能被使用一次
  • 浏览器可以拒绝接受已经存在的支援推送
  • 你可以给其他域名推送资源

如果以上四种缓存都没有命中的话,那么只能发起请求来获取支援了

http 缓存策略(强制缓存、协商缓存)(重点)

浏览器和服务器的通信方式为应答模式(即:浏览器发起HTTP请求-服务器响应该请求),那浏览器怎么确定一个资源该不该被缓存呢,如何去缓存呢?浏览器第一次向服务器发起请求后拿到的请求结果后,将请求结果和缓存标识存入浏览器缓存,所以浏览器对于缓存的处理是根据第一次请求资源时返回的响应头来确定。

总结

  • 浏览器每次发起请求,都会现在浏览器缓存中查找该请求的结果以及缓存标识
  • 浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中

强制缓存

第一次请求后,如果是强制缓存,不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的Network选项中可以看到该请求返回200的状态码,并且Size显示from disk cahe 或 from memory cache。强缓存可以通过设置两种 HTTP Header实现:ExpiresCache-Control

Expires缓存过期的时间,用来指定资源到期的时间,是服务端的具体的时间点。也就是说,Expires=max-age+请求时间,需要和Last-modified结合使用。Expires是Web服务器响应消息头字段,在响应http请求是告诉浏览器在过期时间前浏览器可以直接从浏览器的缓存取资源,而无需再次请求。不过它是HTTP/1的产物,有一个缺陷,受限于本地时间,如果修改了本地时间,可能会造成缓存失效。现在已经被淘汰

  • 同在 Response Headers 中
  • 同为控制缓存过期
  • 已被Cache-Control 代替

请添加图片描述

Cache-Control :Response Header 的参数,是HTTP/1.1中,Cache-Control是重要的规则,主要用于控制浏览器的缓存。例如当Cache-Control:max-age=300(单位是秒)是,就表示在这个请求正确返回的时间的5分钟内再次加载资源,就会命中强缓存。

指令作用
public表示响应可以被客户端和代理服务器缓存
private表示响应只可以被客户端缓存,中间的节点不能缓存
max-age=30缓存30秒后就过期,需要重新请求
s-maxage=30覆盖max-age,作用一样,只在代理服务器中生效;优先级高于max-age
no-store所有的内容都不会被缓存,即不适用轻质缓存,也不使用协商缓存
no-cache可以使用协商缓存,并不是浏览器就不在缓存数据,而是浏览器在使用缓存数据时,需要发起请求验证资源是否还要和服务器保持一致
max-stale=3030秒内,即最大的过期时间,每超出,就还可以命中缓存
min-fresh=30能够忍受的最小新鲜度。min-fresh标示了客户端不愿意接受新鲜度不多于当前的age加上min-fresh设定的时间之和的响应。

可以多个指令配合使用:

请添加图片描述

Expires和Cache-control两者的区别

  • Expires是HTTP/1.0的产物,Cache-Control是HTTP/1.1的产物
  • 它们都是为了实现强缓存
  • 两者同时存在是,Cache-Control优先级高于Expires

协商缓存

顾名思义,协商缓存就是强缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,说一下流程:

  1. 浏览器向服务器发起请求时,会先向浏览器缓存中看是否有这个资源
  2. 如果该请求的缓存结果失败,则只向浏览器返回缓存标识
  3. 携带该资源的缓存标识,向服务端发起HTTP请求
  4. 有两种情况
    1. 返回状态码304,表示该资源无更新(协商缓存成功)
    2. 返回状态码200,表示该资源更新了,返回请求结果(协商缓存失败)
  5. 也有两者情况
    1. 如果是4.1就表示,获取该请求的本地缓存结果,并返回该请求的缓存结果
    2. 如果是4.2就表示将该请求结果和缓存标识存入浏览器缓存中

协商缓存可以通过设置两种HTTP Header实现:Last-ModifiedEtag

Last-Modified和If-Modified-Since

浏览器在第一次访问资源时,服务器返回资源的同时,在reponse header中添加Last-Modified的header,这个值是该资源在服务器上的最后修改时间,浏览器接收后缓存文件和header;,这个值就是Last-Modified;浏览器再次向服务器请求该资源时,会在header中添加If-Modified-Since ,这个值中的内容和 Last-Modified 一样,服务器收到这个请求后,会根据header中的If-Modified-Since中的值与服务器中这个支援的最后修改时间对比,如果没有变化,返回304和空的响应体,直接从缓存读取,如果If-Modified-Since的时间不与服务器中这个资源的最后修改时间一样,说明文件有更新,于是返回新的资源文件和200。

但是Last-Modified的缺点也是有的:

  • 如果本地打开缓存文件,即使没有对文件进行修改,但还是会造成Last-Modified被修改,服务端不能命中缓存导致发送相同的资源
  • 因为Last-Modified只能以秒计时,如果在不可感知的时间内修改完成文件,那么服务端会认为资源还是命中了,不会返回正确的资源

Etag和If-None-Match

Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(有服务器生成),只要资源有变化,Etag就会重新生成。它的流程和Last-Modified一样,只不过将Last-Modified 换成了 EtagIf-Modified-Since 换成 If-None-Match

区别:

  • 首先在精度上,Etag要优于Last-Modified,因为Last-Modified的时间单位是秒,如果某个文件在1秒内改变了多次,那么他们的Last-Modified其实并没有体现出来修改,但是Etag每次都会改变确保了精度;如果是负载均衡的服务器,各个服务器生成的Last-Modified也有可能不一致
  • 第二在性能上,Etag要逊色于Last-Modified,毕竟Last-Modified只需要记录时间,而Etag需要服务器通过算法来计算出一个hash值
  • Etag的优先级比Last-Modified的优先级更高

缓存机制

强制缓存优先于协商缓存进行,若强制缓存(Expiires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified/If-Modified-Since和Etag/If-None-Match),协商缓存有服务器决定是否缓存,若协商缓存失效,那么代表该请求的缓存失效,返回200,重新返回资源和缓存标识,在存入浏览器缓存中;生效则返回304,继续使用缓存。

场景应用

  1. 频繁变动的资源

Cache-Control:no-cache

对于频繁变动的资源,首先需要使用Cache-Control: no-cache使浏览器每次请求服务器,然后配合Etag或者Last-Modified来验证资源是否有效。这样的做法虽然不能节省请求数量,但是能显著显著减少响应数据大小

  1. 不常变化的资源

Cache-Control: max-age=31536000

通常在处理这类资源时,给它们的Cache-Control配置一个很大的max-age=31536000(一年),这样浏览器之后请求相同的URL会命中强制缓存。而为了解决更新的问题,就需要在文件名(或者路径)中添加hash,版本号等动态字符,之后更改动态字符,从而达到更改引用URL的目的,让之前的强制缓存失效(其实并未立即失效,只是不再使用了而已)。向在线的类库(jQuer.min.js,lodash.min.js)均采用这个模式

请添加图片描述

资源标识

  • 在 Response Headers 中, 有两种
  • Last-Modified 资源的最后修改时间
  • Etag 资源的唯一标识(一个字符串,类似人类的指纹)

Last-Modified

请添加图片描述

Etag

请添加图片描述

  • 会优先使用Etag
  • Last-Modified 只能精确到秒级
  • 如果资源被重复生成,而内容不变,则Etag更精确

请添加图片描述

刷新操作方式,对缓存的影响

  • 正常操作:地址栏输入url,查找disk cache 中是否有匹配。如有则使用;如没有则发送网络请求
  • 手动刷新:F5,因为TAB并没有关闭,因此memory cache是可用的,会被优先使用(如果匹配的话)。其次才disk cache
  • 强制刷新:ctrl + F5,浏览器不适用缓存,英雌发送的请求头均带有Cache-control:no-cache(为了兼容,还带了Pragma:no-cache),服务器直接返回200和最新内容。

影响

  • 正常操作:强制缓存有效,协商缓存有效
  • 手动刷新:强制缓存失效,协商缓存有效
  • 强制刷新:强制缓存失效,协商缓存失效

浏览器存储

以前只有Cookie,现在随着网络的发展,出现了WebStorage、IndexedDB这些存储技术。

Cookie

  • Cookie本职工作并非是本地存储,而是"维持状态"。HTTP协议是无状态的,HTTP协议自身不对请求与响应之间的通信状态进行保存。通俗来说,就是服务器不知道用户上一次做了什么,严重的阻碍了交互式Web应用程序的实现。
  • Cookie指某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密)。Cookie是服务器生成,客户端进行维护和存储。通过cookie,可以让服务器知道请求是来源哪个客户端,就可以进行客户端状态的维护,比如登陆状态等。它是附着在HTTP请求上的,在Header中,在浏览器和客户端之间"飞来飞去"
  • Cookie是以键值对的形式存在的
  • Cookie的生成方式:第一次请求服务器的时候,浏览器发出请求,服务器响应请求后,会在响应头里面天机一个Set-Cookie选项,将cookie放入到响应请求中,在浏览器第二次请求的时候,会通过Cookie请求头部将Cookie信息发送给服务器,服务端会辨别用户身份,另外,Cookie的过期时间(Expires)、域、路径、有效期(Max-Age)、使用站点都可以根据需要来指定。有两种生成方式
    • 在 http 响应头里的Set-Cookie指定要存储的Cookie的值。(Domain标识指定了哪些域名可以接受Cookie。如果没有设置domain,就会制动绑定到执行语句的当前域)
    • 通过document.cookie可以读写cookie,以键值对的形式展示
  • Cookie 不够大,大小最多在4KB左右,对于复杂的存储需求来说就不太够用,而且当Cookie超过4KB时,它将面临被裁切的命运。这样看来,Cookie只能用来存取少量的信息。此外很多浏览器对一个站点的cookie个数也是有限制的。这里的Cookie大小4KB,指的是Cookie每一个name=value的value值大概为4KB,4KB并不是一个域名下所有的cookie共享的,而是一个name的大小。
  • 过多的Cookie会带来巨大的性能浪费
  • 由于在HTTP请求中的Cookie是明文传递的,所以会有安全性成问题,除非用HTTPS

其他的属性

属性作用
value如果用于保存用户登录态,应该将该值加密,不能使用明文的用户标识
http-only不能通过JS访问Cookie,减少XSS(跨域脚本)攻击
secure只能在协议为HTTPS的请求中携带
same-site规定浏览器不能跨域请求中携带Cookie,减少CSRF攻击

WebStorage

LocalStorage

  • 保存的数据长期存在,下一次访问该网站的时候,网页可以直接读取以前保存的数据。
  • 大小为5M左右
  • 仅在客户端使用,不和服务端进行通信
  • 接口封装较好
  • 以"键值对"的形式存在。也就是说,每一项数据都有一个键名和对应的值。所有的数据都是以文本格式保存。
    • 存入数据使用setItem方法:localStorage.setItem("key","value")
    • 读取数据使用getItem方法:let data = localStorage.getItem("key")
  • 就使用场景而言,Cookie无法胜任的、简单使用键值对来存取的数据存储任务,都可以交给LocalStorage来做。但是LocalStorage的特点之一是持久,所以我们更倾向于与存储一些内容稳定的资源。

基于上面的特点,LocalStorage可以作为浏览器本地缓存方案,用来提升网页首屏渲染速度

sessionStorage

  • 回话级别的浏览器存储:sessionStorage保存的数据用于浏览器的一次回话,当回话结束(通常是该窗口关闭),数据被清空;sessionStorage特别的一点在于,即便是相同域名下的两个页面看,只要他们不在同一个浏览器窗口打开,那么它们的sessionStorage内容便无法共享。除了保存期限的长短不同,sessionStorage的属性和方法与LocalStorage完全一样
  • 大小为5M左右
  • 仅在客户端使用,不和服务端进行通信
  • 接口封装较好
  • 使用场景:sessionStorage更适合用来存储生命周期和它同步的会话级别的信息。这些信息只适用于当前回话,当你开启新的会话时,它也需要相应的更新和释放。比如微博的sessionStorage就主要是存储你本次会话的浏览足迹,例如:sessionStorage可以存储你上一次的URL地址,这个地址

基于上面的特点,sessionStorage可以有效对标点信息进行维护,比如刷新时,表单形象不丢失。

IndexedDB

IndexedDB是一种低级API,用于客户端存储大量结构化数据(包括文件和blobs)。该API使用索引来实现对该数据的高性能搜索。IndexedDB是一个运行在浏览器上的非关系型数据库。既然是数据库,那就不是5M,10M这样小打小闹级别了。理论上来说,IndexedDB是没有存储上限的(一般不会小于250M)。它不仅可以存储字符串,还可以存储二进制数据。

  • 键值对存储
  • 异步:IndexedDB操作不会锁死浏览器,用户依然可以进行其他操作,这月LocalStorage形成对比,后者的操作是同步的。异步设计是为了防止当量数据的读写,拖慢网页的表现
  • 支持事务:IndexDB支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,浏览器回滚到事务发生之前的状态,不存在只改写一部分数据的情况
  • 同源限制:每一个数据库对应创建它的域名
  • 储存空间大:至少250MB,没有上限
  • 支持二进制储存:不仅仅可以存储字符串,还可以储存二进制数据

WebStorage、cookie、indexedDB区别

特性cookieLocalStoragesessionStorageindexDB
数据生命周期一般有服务器生成,可以设置过期时间除非被清楚,否则一直存在页面关闭就清理除非被清理,否则一直存在
数据存储大小4K5M5M无限
与服务端通信每次都会携带在header中,对于请求的性能有影响不参与不参与不参与

总结

  • cookie的本职工作并非本地存储,而是"维持状态"
  • Web Storage 是 HTML5 专门为浏览器存储而提供的数据存储机制,不予服务端发生通信
  • IndexedDB用于客户端存储大量结构化数据

四种常用跨域

我这里说的是四种常用的,常见的几种请求

注意:浏览器客户端侧才会有跨域,服务端是没有跨域限制的,因为浏览器做了一些限制

什么是同源策略及其限制内容

同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。所谓同源是指"协议+域名(子域名+主域名)+端口"三者相同,即使两个不同的域名指向同一个ip地址,也非同源。

限制了哪些内容呢

  • Cookie、LocalStorage、IndexedDB等存储性内容
  • DOM节点
  • AJAX请求发送后,返回的结果被拦截了

HTML有三个标签是被允许跨域加载资源的

  • <img />
  • <link />
  • <script />

JSONP

其实就是利用<script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的JSON数据。JSONP请求一定需要对方的服务器做支持才可以

  • 仅仅支持get方法具有局限性
  • 可能会遭受XSS攻击,是不安全
  • 实现的流程:
    • 申明一个回调函数,其函数名(如jsonp)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)
    • 创建一个<script>标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=jsonp)
    • 服务器接收到请求后,需求进行特殊的处理: 把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是jsonp,服务器准备好的数据传递给jsonp的参数
    • 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作
function jsonp({ url, params}) {
  let fn = Date.now() + Math.random().toString().substr(2, 5);
  return new Promise((resolve, reject) => {
    let script = document.createElement('script')
    window[fn] = function(data) {
      resolve(data)
      document.body.removeChild(script)
    }
    params = { ...params,callback:fn } // wd=b&callback=show
    let arrs = []
    for (let key in params) {
      arrs.push(`${key}=${params[key]}`)
    }
    script.src = `${url}?${arrs.join('&')}`
    document.body.appendChild(script)
  })
}
jsonp({
  url: 'http://localhost:3000/hi',
  params: { msg: 'hello word' },
}).then(data => {
  console.log(data)
})

可以通过callback 的形式拿到数据

CORS

CORS需要浏览器和后端同时支持。而浏览器会自动进行CORS通信,实现CORS通信的关键是后端。只要后端实现了CORS,就实现了跨域。而服务端只需要在响应头header中加上Access-Control-Allow-Origin: * 就可以开启CORS,该属性表示哪些域名可以访问资源(用,(英文逗号)),如果设置了*号表示所有网站都可以访问资源。

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

简单请求

只要同时满足以下两大条件,就属于简单请求

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

复杂请求

不符合简单请求的就都是复杂请求

复杂请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求,该请求是OPTIONS请求方法的,通过该请求来了解该接口有哪些方法可以被响应,是否允许跨域。如果options获得的回应时拒绝性质的,如404、403、500等状态,就会停止之后的真实请求。

WebSocket

Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。

原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

代理

我们前面也说过了,服务器之间请求是没有跨域的说法的,所以我们可以在同源的情况下布置一个服务器,客户端的请求打到这个服务器上,再由这个服务器去目标服务器请求数据。大概步骤如下:

  • 客户端发送请求
  • 代理服务器接收到请求,并将该请求转发给目标服务器
  • 目标服务器返回数据给代理服务器,代理服务器拿到数据
  • 代理服务器再将数据返回给客户端

我们常用的就是Nginx进行代理

总结

  • CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方案
  • JSONP只支持GET请求,JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据
  • 代理都是通过同源策略对服务器不加限制这个点
  • 比较多的跨域方案是CORS和Nginx反向代理

web安全攻防

XSS(重点)

XSS (Cross-Site Scripting),跨站脚本攻击,因为缩写和 CSS重叠,所以只能叫 XSS。跨站脚本攻击是指通过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或JavaScript进行的一种攻击。

原理就是:恶意攻击者往Web页面里插入恶意可执行网页脚本代码,当用户浏览页之时,嵌入其中Web里面的脚本代码会被执行,从而可以打到攻击者盗取用户信息或其他侵犯用户安全隐私的目的。

XSS的攻击方式有很多,但大致可以分为一下几种类型

非持久型XSS(反射型XSS)

非持久型XSS漏洞,一般是通过个别人发送带有脚本代码参数的URL,当URL地址被打开时,特有的恶意代码参数被HTML解析、执行。

不过一些浏览器内置了一些XSS过滤器,可以防止大部分反射型XSS攻击

特点如下:

  • 即时性,不经过服务器存储,直接通过HTTP的GET和POST请求就能完成了一次攻击,拿到用户隐私数据。
  • 攻击者需要诱骗点击,必须要通过用户点击链接才能发起
  • 反馈率低,所以较难发现和响应修复
  • 盗取用户敏感保密信息

防御的方法有,如下:

  • Web 页面渲染的所有内容或者渲染的数据都必须来自于服务端。
  • 尽量不要从 URLdocument.referrerdocument.forms 等这种 DOM API 中获取数据直接渲染。
  • 尽量不要使用 eval, new Function()document.write()document.writeln()window.setInterval()window.setTimeout()innerHTMLdocument.createElement() 等可执行字符串的方法。
  • 如果做不到以上几点,也必须对涉及 DOM 渲染的方法传入的字符串参数做 escape 转义。
  • 前端渲染的时候对任何的字段都需要做 escape 转义编码。

持久型XSS(存储型XSS)

持久型XSS漏洞,一般存于Form表单提交等交互功能,如文章留言,提交文本信息等,黑客利用的XSS漏洞,将内容经正常功能提交进入数据库持久保存,当前端页面获得后端从数据库中读出的注入代码时,恰好将其渲染执行。

特点:

  • 持久型,植入在数据库中
  • 盗取用户铭感私密信息
  • 前端拿到后端数据没做转义直接渲染成DOM

例如在评论时输入<script>alert(1)</script> ,主要注入页面方式和非持久型XSS漏洞类似,只不过持久型的不是来源于URL,referer,forms,而是来源于后端从数据库中读出来的数据。持久型XSS攻击不需要诱骗点击,黑客只需要在提交表单的地方完成注入即可,但是这种XSS攻击的成本相对还是很搞。

攻击成功需要同时满足以下几个条件:

  • POST请求提交表单后端没做转义直接入库。
  • 后端从数据库中取出的数据没做转义直接输出给前端
  • 前端拿到后端数据没做转义直接渲染成DOM

防御方法如下:

  • CSP :本质其实就是一个白名单,开发者明确告诉浏览器哪些外部资源可以加载和执行。我们只需要配置规则,如何拦截是有浏览器自己实现的。我们可以通过这种方法来尽量减少XSS攻击。有两种方式来开启CSP
    • 设置HTTP Header中的Content-Security-Policy
    • 设置meta标签的方式<meta http-equive="Content-Security-Policy">
    • 详细的细节可以去:Content-Security-Policy的文档
  • 转义字符:记住用户的输入永远是不可信任的,最普遍的做法就是转义输入输出的内容,对于引号、尖括号、斜杠进行转义。
function escape(str) {
  str = str.replace(/&/g, '&amp;')
  str = str.replace(/</g, '&lt;')
  str = str.replace(/>/g, '&gt;')
  str = str.replace(/"/g, '&quto;')
  str = str.replace(/'/g, '&#39;')
  str = str.replace(/`/g, '&#96;')
  str = str.replace(/\//g, '&#x2F;')
  return str
}

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

const xss = require('xss')
let html = xss('<h1 id="title">XSS Demo</h1><script>alert("xss");</script>')
// -> <h1>XSS Demo</h1>&lt;script&gt;alert("xss");&lt;/script&gt;
console.log(html)

以上示例使用了 js-xss 来实现,可以看到在输出中保留了 h1 标签且过滤了 script 标签。

  • HttpOnly Cookie

这是预防XSS攻击窃取cookie最有效的防御手段。Web应用程序在设置cookie时,将其属性设为HttpOnly,就可以避免该网页的cookie被客户端恶意JavaScript窃取,保护用户cookie信息。

CSRF(重点)

CSRF(Cross Site Request Forgery),即跨站请求伪造,是一种常见的Web攻击,它利用用户已登录的身份,在用户毫不知情的情况下,以用户的名义完成非法操作。

原理

  1. 用户在浏览器中登录信任网站A
  2. 用户通过验证,A返回请求,在响应头把cookie带上
  3. 用户在没有登出A网站的情况下,访问危险网站B
  4. B请求访问A网站,发出一个request请求
  5. 根据B网站在步骤4中的请求,浏览器带着step2产生的cookie访问A

完成CSRF攻击必须要有三个条件:

  • 用户已经登录了站点A,并在本地记录了cookie
  • 在用户没有登出站点A的情况下(也就是cookie生效的情况下),访问了恶意攻击者提供的引诱
  • 站点A没有做任何CSRF防御

举一个例子:当我们登入转账页面后,突然眼前一亮"XXX隐私照片,不看后悔一辈子"的链接,你马上就耐不住内心躁动,立马点击了该危险的网站,但当这页面一加载,便会执行submitForm这个方法来提交转账请求,从而将10块转给黑客。

防御方法如下:

  • Get 请求不对数据进行修改
  • 不让第三方网站访问到用户Cookie
  • 阻止第三方网站请求接口
  • 请求时附带验证信息,比如验证码或者Token
  1. SameSite:可以对Cookie设置SameSite属性。该属性表示Cookie不随着跨域请求发送,可以很大程度减少CSRF的攻击,但是该属性目前并不是所有浏览器都兼容。
  2. Referer Check:HTTP Referer是header的一部分,当浏览器向web服务器发送请求时,一般会带上Referer信息告诉服务器是从哪个页面链接过来的,服务器籍此可以获得一些信息用于处理。可以通过检查请求的来源来防御CSRF攻击。正常请求的referer具有一定规律,如在提交表单的referer必定是在该页面发起的请求。所以通过检查http包头referer的值是不是这个页面,来判断是不是CSRF攻击,CSRF也是有缺陷的,但是可以通过Referer Check来监控CSRF攻击的发生
  3. Anti CSRF Token
    1. 目前比较完善的解决方案是加入Anti-CSRF-Token。即发送请求时在HTTP 请求中以参数的形式加入一个随机产生的token,并在服务器建立一个拦截器来验证这个token。服务器读取浏览器当前域cookie中这个token值,会进行校验该请求当中的token和cookie当中的token值是否都存在且相等,才认为这是合法的请求。否则认为这次请求是违法的,拒绝该次服务。
    2. 这种方法相比Referer检查要安全很多,token可以在用户登陆后产生并放于session或cookie中,然后在每次请求时服务器把token从session或cookie中拿出,与本次请求中的token 进行比对。由于token的存在,攻击者无法再构造出一个完整的URL实施CSRF攻击。但在处理多个页面共存问题时,当某个页面消耗掉token后,其他页面的表单保存的还是被消耗掉的那个token,其他页面的表单提交时会出现token错误。
  4. 验证码:应用程序和用户进行交互过程中,特别是账户交易这种核心步骤,强制用户输入验证码,才能完成最终请求。在通常情况下,验证码够很好地遏制CSRF攻击。但增加验证码降低了用户的体验,网站不能给所有的操作都加上验证码。所以只能将验证码作为一种辅助手段,在关键业务点设置验证码。

点击劫持

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

特点

  • 隐蔽性较高,骗取用户操作
  • “UI-覆盖攻击”
  • 利用iframe或者其它标签的属性

原理

用户在登陆 A 网站的系统后,被攻击者诱惑打开第三方网站,而第三方网站通过 iframe 引入了 A 网站的页面内容,用户在第三方网站中点击某个按钮(被装饰的按钮),实际上是点击了 A 网站的按钮。

防御方法如下:

  • X-FRAME_OPTIONS
    • X-FRAME-OPTIONS是一个 HTTP 响应头,在现代浏览器有一个很好的支持。这个 HTTP 响应头 就是为了防御用 iframe 嵌套的点击劫持攻击
    • 该响应头有三个值可选,分别是:
      • DENY,表示页面不允许通过 iframe 的方式展示
      • SAMEORIGIN,表示页面可以在相同域名下通过 iframe 的方式展示
      • ALLOW-FROM,表示页面可以在指定来源的 iframe 中展示
  • JavaScript方式防御

URL跳转漏洞

定义:借助未验证的URL跳转,将爷们引导到不安全的第三方区域,从而导致的安全问题

原理

黑客利用URL跳转漏洞来诱导安全意识低的用户点击,导致用户信息泄露或者资金的流失。其原理是黑客构建恶意链接(链接需要进行伪装,尽可能迷惑),发在QQ群或者是浏览量多的贴吧/论坛中。安全意识低的用户点击后,经过服务器或者浏览器解析后,跳到恶意的网站中。

实现方式

  • Header头跳转
  • Javascript跳转
  • META标签跳转

如何防御

  1. referer的限制:如果确定传递URL参数进入的来源,我们可以通过该方式实现安全限制,保证该URL的有效性,避免恶意用户自己生成跳转链接
  2. 假如有效性验证Token:我们保证所有生成的链接都是来自于我们可信域的,通过在生成的链接里加入用户不可控的Token对生成的链接进行校验,可以避免用户生成自己的恶意链接从而被利用,但是如果功能本身要求比较开放,可能导致有一定的限制。

SQL注入(重点)

SQL注入是一种常见的Web安全漏洞,攻击者利用这个漏洞,可以访问或修改数据,或者利用潜在的数据库漏洞进行攻击

原理

本质就是数据和代码未分离,即数据当做了代码来执行。例如 ' -- 就表示闭合和注释的含义

危害

  • 获取数据库信息
    • 管理员后台用户名和密码
    • 获取其他数据库敏感信息:用户名、密码、手机号码、身份证、银行卡信息……
    • 整个数据库:脱裤
  • 获取服务器权限
  • 植入Webshell,获取服务器后门
  • 读取服务器敏感文件

防御

  • 严格限制Web应用的数据库的操作权限,给此用户提供仅仅能够满足其工作的最低权限,从而最大限度的减少注入攻击对数据库的危害
  • 后端代码检查输入的数据是否符合预期,严格限制变量的类型,例如使用正则表达式进行一些匹配处理。
  • 对进入数据库的特殊字符(’,",,<,>,&,*,; 等)进行转义处理,或编码转换。基本上所有的后端语言都有对字符串进行转义处理的方法,比如 lodash 的 lodash._escapehtmlchar 库。
  • 所有的查询语句建议使用数据库提供的参数化查询接口,参数化的语句使用参数而不是将用户输入变量嵌入到 SQL 语句中,即不要直接拼接 SQL 语句。例如 Node.js 中的 mysqljs 库的 query 方法中的 ? 占位参数。

OS命令注入攻击(了解)

OS命令注入和SQL注入差不多,只不过SQL注入是针对数据库的,而OS命令注入是针对操作系统的。OS命令注入攻击指通过Web应用,执行非法的操作系统命令达到攻击的目的。只要在能调用Shell函数的地方就有存在被攻击的风险。倘若调用Shell时存在疏漏,就可以执行插入的非法命令。

命令注入攻击可以向Shell发送命令,让Windows或Linux操作系统的命令行启动程序。也就是说,通过命令注入攻击可执行操作系统上安装着的各种程序。

面试题

http 常见的状态码有哪些?

上面提到过

http 常见的 header 有哪些?

上面提到过

三次握手第一次可以携带数据吗?

不可以的,三次握手还没有完成不能携带数据。

为什么必须要三次握手,两次可以吗?

不可以,第一次握手:客户端发送网络包,服务端收到。表示:客户端的发送能力、服务端的接受能力是正常的。第二次握手:服务端发包,客户端收到了。就表示:服务端的接收、发送能力,客户端的接收、发送能力是正常的。如果(客户端不回复)没有第三次握手就不能确认客户端的接收能力是否正常。第三次握手:客户端发包了,服务端收到了。就表示:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。

三次握手的作用

  • 确认服务器端和客户端的发送及接收能力正常
  • 去人自己的初始化序列号,为后面的可靠传输做准备
  • 如果是https协议,三次握手过程中还会进行数字证书的验证

第一次握手中指定客户端的初始序列号,这个序列号是固定的吗?

三次握手的第一个重要功能就是客户端和服务端交换ISN,接下来接收数据的时候如何按照序号组装数据,如果ISN是固定的,攻击者就很容易确定后续的确定号,因此为了安全ISN都是动态生成的。

什么是 Restful API

上面提到过

描述一下 http 的缓存机制(重要)

上面提到过

一次完整的HTTP服务过程

当我们在web浏览器的地址栏中输入:www.baidu.com ,具体发生了什么?

  1. www.baidu.com这个网址进行DNS域名解析,得到对应的IP地址
  2. 根据这个IP,找到对应的服务器,发起TCP的三次握手
  3. 建立TCP连接后发起HTTP请求
  4. 服务器响应HTTP请求,浏览器得到html代码
  5. 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等)(先得到html代码,才能去找这些资源)
  6. 浏览器对页面进行渲染呈现给用户
  7. 服务器关闭TCP连接,四次挥手

注:

  1. DNS怎么找到域名的?

DNS域名解析采用的是递归查询的方式,过程是,先去找DNS缓存->缓存找不到就去找根域名服务器->找不到,根域名又会去下一级,这样递归查找之后,找到了,给我们的web浏览器

  1. 为什么HTTP协议基于TCP来实现?

TCP是一个端到端的可靠的面向连接的协议,HTTP基于传输层TCP协议不用担心数据传输的问题(当发生错误时,会重传)

  1. 最后一步浏览器是如何对页面进行渲染的
  1. 解析html文件构成DOM树
  2. 解析css文件构成渲染树
  3. 边解析,边渲染
  4. JS单线程运行,JS有可能修改DOM结构,意味着JS执行完成前,后续所有资源的下载是没有必要的,所有JS是单线程,会阻塞后续资源下载

各个步骤具体细节

DNS解析(域名解析服务器)

  1. 首先会搜索浏览器自身的DNS缓存(缓存时间比较短,大概只有1分钟,且只能容纳1000条缓存)
  2. 如果浏览器知识的缓存里面没有找到,那么浏览器会搜索系统自身的DNS缓存
  3. 如果还没有找到,那么尝试从hosts文件里面去找
  4. 在前面三个过程都没获取到的情况下,就递归地去域名服务器去查找,具体过程如下:

请添加图片描述

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-08-12 17:02:28  更:2021-08-12 17:04:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 19:25:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码