http整理

177 阅读11分钟

跨域

CORS 跨域资源共享,服务端支持

对于简单请求,比如 GETPOST,浏览器在请求时候会在头部带上 Origin,指明请求页面的源,后端通过判断若决定响应,则再响应头加入 Access-Control-Allow-Origin,值可以使源,也可以是 *

对于其他请求,通过叫做预检请求的机制进行验证,在请求前会先发送一个 Options 请求,询问服务器支持的类型,请求头部包含以下字段

  • Origin 同简单请求
  • Access-Control-Request-Method 请求希望支持的方法
  • Access-Control-Request-Headers 自定义头部

服务器判断后如果决定响应,则需要在头部返回以下信息

  • Access-Control-Allow-Headers 支持的自定义头部
  • Access-Control-Allow-Methods 支持的方法
  • Access-Control-Allow-Origin 同简单请求

图片探测

即通过设置 <img src='xxx' />来实现请求,通常用来触发上报,跟踪用户

  • 只能发送 GET 请求
  • 无法接受消息

JSONP

实现原理,通过 <script src='xxxxx.js' /> 发送请求给服务器,服务器返回的字符串中调用指定回调函数传送数据

浏览器代码

<script>
  function hello(data) {
    console.log(data);
  }
</script>
<script src="http://127.0.0.1:3000/index.js?callback=hello"></script>

服务器代码

const http = require('http');
const url = require('url');

http.createServer(function (request, response) {
  const urlParse = url.parse(request.url, true);
  const callback = urlParse.query.callback

  response.end(`${callback}('hello world')`);
}).listen(3000);

执行成功会输出 hello world

  • JSONP同样只能执行简单的 GET 请求,但是可以带参数,
  • 由于拉取了可执行代码,安全性需要确定
  • 确认失败比较困难

其他

iframe:打开一个 iframe,两个页面之间通过 postMessage 进行交互

Cookie相关

http是无状态协议,cookie是管理服务器与客户端之间状态的东西

Set-Cookie

服务器端告诉客户端需要存储在客户端的cookie值,基本格式

Set-Cookie: name=xxx;Max-age=60;Secure

name=xxx表示key和value,后面;分隔的是参数,约定了cookie怎么存在,具体的参数有

  • Max-Age: 常用的属性,表示cookie存在的时长,以秒为单位
  • Path: 指定可访问Cookie的目录,如设定Path=/home,则只有/home 才能访问该值
  • Domain: 指定可访问该值得域名,这里的域名是指同一主机下的域名,例如我们设置了 Domain=test.com,这时候 api.test.com, www.test.com都能访问该值
  • HttpOnly: 使JavaScript无法访问该值,尝试后 document.cookie 无法获取该值
  • Expires: 设置Cookie的过期时间,这个属性感觉没有Max-Age好用,这个你得把具体的时间计算出来,例如:Expires=Wed, 13-Jan-2021 22:23:01 GMT
  • Secure: 只有在https请求时cookie才会生效
  • SameSite: 跨站请求时是否被发送,可以防止CSRF攻击,SameSite 取值有以下
    • Strict仅允许一方请求携带 Cookie,即浏览器将只发送相同站点请求的 Cookie,即当前网页 URL 与请求目标 URL 完全一致。
    • Lax允许部分第三方请求携带 Cookie
    • None无论是否跨站都会发送 Cookie

Cookie

请求中,用Cookie字段告诉服务器本地存储的Cookie值,在浏览器中这一步实际是浏览器做了的,这就简单得多的,就是key=value的形式,以;分隔

Cookie: age=222; name=yyd

注意事项

  • cookie存储是有限制的,不同浏览器cookie的大小和最大存储数量不一致
  • Max-AgeExpires 同时存在优先 Max-Age

安全相关

由于http不具备必要的安全功能,且http请求的诸多信息都是可篡改的,所以我们需要了解不安全的地方在那儿以及应对手法

SQL注入

SQL注入是指针对Web应用使用的数据库,通过运行非常的SQL而产生的攻击

影响:

  • 非法查看或篡改数据库内的数据
  • 规避认证
  • 执行和数据库服务器业务关联的程序等

SQL注入的作用端是在后端,但产生通常是通过前端传送给后端的数据,所以这里 前端应该对用户输入的值进行校验,后端应该对前端传送过来的值进行校验

OS命令注入

和SQL注入类似,SQL注入是攻击者执行非法的SQL语句,而OS是指攻击者执行非法的操作系统命令,达到最服务器造成攻击的目的

跨站脚本攻击( Cross-Site Scripting, XSS )

通过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或JavaScript进行的一种攻击

造成影响:

  • 利用虚假输入表单片区用户个人信息,
  • 利用脚本窃取用户的Cookie值,被害者在不知情的情况下,帮助攻击者发送而已请求。
  • 显示伪造的文章或图片

案例: 动态生成HTML处发生,这个很常见,比如我们一个input框,需要实时显示用户输入的内容

  <div id="root">
    <div>111</div>
  </div>

  <input type="text" id="input">
  <div id="preview"></div>
  <script>
    document.getElementById('input').onchange= (e) => {
      document.getElementById('preview').innerHTML = e.target.value;
    }
  </script>

当我们在input中输入html的时候就会有问题

如果输入JavaScript代码会更危险,解决方法就是我们可以在输出用户的输入的时候做转义,或者检测到html字符的时候不做输出,下面提供一个html转义的方法

function htmlEscape(text) {
  return text.replace(/[<>"&]/g, function (match, pos, originalText) {
    switch (match) {
      case "<": return "&lt;";
      case ">": return "&gt;";
      case "&": return "&amp;";
      case "\"": return "&quot;";
    }
  });
}

url的查询字段和hash值也是用户可以随意篡改的地方,所以在取的时候也应该进行校验

跨站点请求伪造 (Cross-Site Request Forgeries, CSRF)

攻击是指攻击者通过设置好的陷阱,强制对已完成认证的用户进行非预期的个人信息或设定信息等某些状态更新。

加入我们网站中有一个更新评论的get接口

http://example.com/msg?q=hello

攻击网站将这段代码以图片的形式放到B网站上

<img src="http://example.com/msg?q=hello" />

如果A用户在已经登录我们网站的情况下,再去访问B网站,就会在用户不知情的情况下发起一个更新评论的请求。

如果请求是post呢?这时候攻击者需要在自己的网站里整个form表单,将action指向我们网站

<form action="http://example.com/msg" method="POST">
  <input type="text" name="q">
  <input type="submit" value="OK">
</form>

防御手段

  • Get 请求不对数据进行修改
  • Cookie 字段中加入 SameSite (兼容性会有问题)
  • 服务器验证 Referer,后台接收到请求的时候可通过请求头里的Referer字段校验
  • 验证token,服务器下发一个随机token,请求的时候每次带上这个token,比如曾经用Laravel框架的时候就可以在渲染时候通过 csrf_token 方法获取到token

开放重定向

如我们有个url

http://example.com/login?redirect=http://example.com/home

这里意思是登录后跳转到home这个路由,但攻击者如果通过篡改redirect就可能将用户引导至其他危险网站,同样需要我们在使用的时候进行充分的校验

HTTP 1.0、HTTP 1.1、HTTP2.0

HTTP 1.0

  • 比较老的协议,浏览器与服务器只保持短暂的连接,每次请求都会重新建立连接,即每次都会经历握手挥手过程
  • 解决连接复用的问题,提出了 keep-alive:connection,但引入新的问题,队头阻塞,即使前面的请求响应未完成阻塞后面的请求

解决队头阻塞的一个方案是,部署多个域名,比如api接口部署在api.xxx.com,文件部署在其他域名下

HTTP 1.1

  • 默认开启了长连接
  • 新的缓存机制 cache-control
  • host,虚拟主机在同一ip,使用 host 区分
  • 管道机制,可以同时发起多个请求,但接收响应顺序接收,存在阻塞问题

HTTP 2.0

  • 请求和响应多路复用,解决队头阻塞问题
  • 首部压缩,压缩算法 + 共同维护索引
  • 服务器推送

状态码

描述返回的请求结果,借助状态码,用户可以知道服务端是正常处理了请求还是出现了错误,总结几个常见的

组成

3位数字和原因短语,例如 200 OK

数字中的第一位指定了响应类别,后两位为分类

2XX

2XX的响应结果表明请求被正常处理

200 OK

请求被正常处理

204 No Content

请求被正常处理,但在返回的响应中部包含主体部分,一般在只需要从客户端往服务器发送信息,而对客户端不需要发送新信息内容时候使用

206 Partial Content

表示客户端进行了范围请求,服务器成功执行,响应报文中包含 Content-Range 指定范围的实体内容

3XX

301 Moved Permanently

永久重定向,如果保存为书签,应根据Location首部字段提示的URI重新保存。这里刚开始我以为浏览器会自动给你修改书签,事实是我想多了,修改书签只是建议,浏览器并不会自动帮你更新书签

302 Found

临时重定向,临时性质的重定向,以后还可能改动,不建议更改书签

4XX

表示客户端发生错误

400 Bad Request

请求错误,具体错误原因未知

401 Unauthorized

请求需要认证

403 Forbidden

访问权限被拒绝

偶尔会被人问到403和401的区别,401就是在需要认证未认证的时候返回,403则是已经认证,但是权限出现问题的时候返回

404 Not Found

无法找到请求的资源,也可在服务器拒绝请求但不想给出理由时返回(就是制造迷惑行为,俺是这么理解的)

5XX

服务器本身发生错误

500 Internal Server Error

服务器执行请求发生错误

503 Server Unavailable

服务器暂时处于超负载或者正在停机维护,可以通过返回 Retry-After 字段告诉客户端什么时间段重试 Retry-After

504 Gateway Timeout

超时

Storage

LocalStorage 和 SessionStorage,并不属于http,偶然看到就记一下,主要区别

  • SessionStorage 独立于网页窗口存在,随窗口关闭一起清除,不会跨窗口共享
  • LocalStorage 同一个域共享一份数据,只有清除才会消失

握手

TCP三次握手

网上有很多经典解释,有很多图,我这里放一张我通过抓包获得的图吧

  • 客户端发送一个 SYN 标记的包,Seq 初始序列号 x,发送完成后客户端进入 SYN_SEND 状态。
  • 服务器返回ACK应答,同时还要发送一个 SYN 包回去。Ack = x + 1,表示确认,Seq = y。发送完成后服务端进入 SYN_RCVD 状态。
  • 客户端确认服务器收到了自己发起连接的包,同时返回 ACK 应答服务器

为什么是三次

客户端和服务端为了确认对方能正常的接收和发送

TCP四次挥手

  • 客户端发起连接释放请求,设置 FIN1,带上Seq = u,客户端进入 FIN-WAIT-1
  • 服务器收到 FIN 后,会返回一个 ACK,并带上 Ack = u + 1, 服务器进入 CLOSE-WAIT,客户端收到后进入 FIN-WAIT-2
  • 服务端准备好后,发送结束连接请求,设置 FIN1,带上确认号Ack = u + 1、序号 Seq = w,进入 LAST-ACK
  • 客户端收到服务端结束请求,发送一个确认包,服务器收到后进入CLOSED,客户端会进入一个等待,等待服务端需要重传的 ACK,如果在一个固定时间内没收到,则会进入CLOSED

为什么是四次

服务端在收到结束连接请求时,可能部分数据还正在传输,所以需要一个准备阶段,只有等到准备好了,才会告诉客户端我准备好了,结束连接

客户端为什么最后需要等待

这里是为了确认客户端最后一个 ACK 到达了服务端,如果服务端没收到,则会要求重发,所以客户端需要等待

缓存

  • 强缓存:浏览器不与服务器协商直接取缓存
  • 协商缓存:浏览器与服务器协商取得缓存

协商缓存

主要用到http头部字段 EtagLast-Modified,每次收到一个请求时候,服务器将资源最后修改时间 Last-Modified 和根据资源计算出来的 Etag 值放到header头里面,客户端在收到这两个值后会保存这两个值、并在下次请求的时候将两个值分别放到 If-Modified-SinceIf-None-Match 中,服务器收到后进行比对,如果都一样则返回304使得浏览器使用缓存,如果有变化则返回新资源和 200

使用协商缓存时需要设置 cache-control: no-cache

强缓存

使用字段 Cache-control ,取值如下

  • no-cache 缓存前需要协商
  • no-store 禁用缓存
  • public 允许任何用户缓存
  • private 只允许浏览器缓存
  • max-age 响应最大age