8月更文挑战 | 手撕HTTP以及HTTPS

620 阅读16分钟

这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战

前言

http网络协议一直是我们在工作中不可避免的一个环节,所以最近搞了一本入门款http图解读了一个月今天来分享一下学习成果,一来对自己的学习做个总结,二来记录一下学习的经历,本篇文章主要包含http协议的构成以及通信的原理和https具体的区别,之后会手动实现一些网络协议中的攻击方式以及常用的防范办法,有哪些地方不对的大家可以及时指出🙏

介绍(来自百科)

超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII形式给出;而消息内容则具有一个类似MIME的格式。这个简单模型是早期Web成功的有功之臣,因为它使开发和部署非常地直截了当。

http协议的构成

请求报文、响应报文、请求方式、状态码

  • 请求体相关字段

    1、Accept 客户端可以接收的数据类型一般是任意类型 */*
    2、Accept-Encoding 客户端可以接受的编码方式一般是 gzip, deflate, br
    3、Accept-Language 客户端可以接收的语言类型 zh-CN,zh
    4、Connection 是否开启了长连接
    5、Content-Type 与服务端之间通信使用的方式一般有几种类型 jsonformDate
    6、Cookie 保存http的状态协议 (因为http本身是无状态的有了cookie之后才可以存储状态)
    7、Host 目标服务器的域名
    8、User-Agent 在这里面可以获取到客户端浏览器内核信息
    9、if-none-match 协商缓存主要配合Etag使用
    10、If-Modify-Since 协商缓存主要配合last-Modify使用

  • 响应体相关字段

    1、Cache-Control http1.1服务器设置的强缓存过期时间(具体可以看我上一篇文章客户端缓存详解juejin.cn/post/698793…
    2、Expires http1.0服务器设置的一个GMT(格林威治)时间(会和本地时间做校验验证缓存是否过
    3、Last-Modified 服务器设置的协商缓存二次请求的时候客户端会通过If-Modify-Since带到服务端校验
    4.Location 主要用于重定向返回302或307状态码的时候会根据Location提供的url进行重定向
    5、Set-Cookie 设置和页面关联的Cookie,主要用于在客户端创建一个Cookie,之后请求的时候会默认带上
    6、Allow 服务器支持的请求方法
    7、Content-Encoding 服务器支持的文档的编码方式,只有在解码之后才可以得到Content-Type头指定的内容类型
    8、Content-Length 响应内容的长度当浏览器使用持久http连接时才需要这个数据
    9、Date 当前的GMT时间
    10、Server 服务器名字
    11、Refresh 表示浏览器应该在多久之后刷新页面或者跳转指定的页面以秒为单位(可以通过setHeader指定跳转页面)

  • 请求方式

    HTTP1.0

    1、GET 请求指定的页面信息返回实体主体,并且是明文传输拼接在域名后面请求

    2、POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中

    3、HEAD 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头

    HTTP1.1

    4、DELETE 请求服务器删除Request-URI所标识的资源。

    5、PUT 从客户端向服务器传送的数据取代指定的文档的内容。

    6、OPTIONS 允许客户端查看服务器的性能

  • 状态码

    1xx服务器收到请求,需要请求者继续执行操作

    100 继续。客户端应继续其请求

    101 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议

    2xx一般是请求成功接收并处理

    200 请求成功一般用于GETPOST

    201 成功请求并且创建了资源

    202 已经接受请求,但未处理完成

    204 服务器处理成功但是没有返回内容 no content

    3xx重定向,需要进一步操作

    301 永久重定向,搜索引擎在抓取新内容的同时也将旧的网址替换为重定向之后的地址,地址会在响应信息中返回

    302 临时重定向,搜索引擎会抓取新的内容而保留旧的网址,因为服务器返回302代码,搜索引擎认为网址只是暂时的

    304 返回的是缓存资源,当进行协商缓存查找并且没有过期的时候会返回304

    307 临时重定向与302类似使用GET请求进行重定向(大家可以看看百度直接访问www.baidu.com会重定向到https下面)

    4xx客户端错误,多为请求参数有问题或者请求路径有误

    400 客户端请求错误一般我遇到这个大多数都是参数传错了(其实还是api文档写的不清楚🤔)

    401 客户端请求没有权限需要认证之后请求

    403 服务端拒绝执行客户端的请求(就好像我虽然理解了但是我不做的样子)

    404 客户端请求接口路径错误导致404

    5xx为服务端错误,可能是服务器挂了或者在处理参数的过程中发生错误

    500 服务器内部错误,无法顺利完成请求

    502 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应

    503 由于超载或系统维护,服务器暂时无法处理客户的请求

    504 一般我遇到的时候就是服务器挂了,或者在重启的时候会报504

    HTTP以及HTTPS通信原理

  • HTTP

    http默认端口号是80建立在tcp之上进行通信中间从应用层http =>传输层tcp或者udp =>网络层 => ip => 链路层 连接到另一台机器,这是一个正常的通信过程内部又经过DNS域名查询以及tcp的三次握手但是http在数据传输的时候是明文传输尽管post将参数放到实体中也是可以通过抓包工具抓到的

    展开讲一下udp和tcp的区别

    1、tcp是点对点连接也就是一对一,而UDP可以一对多、一对一、多对多连接

    2、tcp的缺点是传输速度较慢因为中间需要经过一系列的连接过程,好处传输稳定性很高适用于邮件发送等

    3、udp传输速度会快一些但是安全性相对来说没有TCP那么高,适用于视频广播等

  • HTTPS

    https默认端口是443其实本身并不算一个协议因为它等于HTTP+TLS/SSL,连接过程除了有上述http的操作,之后在传输的过程中增加了非对称加密算法和对称加密算法,通常https主要用来传输一些加密的信息因为本身https对于cpu的消耗要高于http的并且申请相应的CA证书都需要经费,所以如果不是加密信息大部分使用http就可以

    展开讲一下SSL、TLS、对称加密、非对称加密、CA证书

    1、SSL(Secure Sockets Layer) 安全套接字协议,是一个不依赖于平台和运用程序的协议,基于HTTP下TCP之上的一个协议层,位于TCP/IP协议与各种应用层协议之间,在网络七层模型中处于会话层(第 5 层),为数据通信提高安全支持,主要任务是提供私密性,信息完整性和身份认证

    2、TLS(Transport Layer Security)安全传输层协议,在SSL更新到3.0时,IETFSSL3.0进行了标准话并更名为TLS,那么也就是说TLSSSL更新之后的一个安全协议,主要也用于在两个通信应用程序之间提供保密性和数据完整性,整体来说TLS非常类似SSl3.0,只是对SSL3.0做了些增加和修改。

    3、在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥,于是就出现了非对称加密算法

    4、非对称加密算法秘钥成对出现,分为公钥和私钥,公钥加密需要私钥解密,私钥加密需要公钥解密,https交互的过程中需要对称加密生成的秘钥进行交互,但是在对称加密的协商过程中需要非对称加密的方式进行加密,但是也有可能出现被人劫持的可能于是出现了CA证书

    5、因为在非对称加密协商的过程中也有可能被篡改的可能(俗称中间人攻击)所以这时候就需要数字证书(CA证书)签发机构颁发的证书来保证加密过程的安全

    6、CA是证书的签发机构,它是公钥基础设施(Public Key Infrastructure,PKI)的核心。CA是负责签发证书、认证证书、管理已颁发证书的机关。

  • 具体连接通信过程

    进行域名分析在DNS系统进行查询得到对应的ip地址,进行tcp三次握手连接(为什么是三次🤔因为两次不够四次浪费)具体过程可以看下图,tcp连接建立完成之后浏览器就会向服务器发送请求命令,正常进行通信在http1.0的时候每次通信完成关闭,在关闭的时候会进行四次挥手,到http1.1新增connection:keep-alive保持长连接可以在接下来的通信中不需要在进行tcp连接

    三次握手过程

    1、首次客户端会发送一个SYN码到服务器

    2、服务器会返回一个ACK码由客户端传过来的SYN+1返回

    3、目的就是为了能让双方都知道我们是可以正常通信的

WechatIMG666.jpeg

HTTP0.9->1.0->1.1->2.0的心路历程

  • HTTP0.9

    1991年出现的http0.9版本是http的第一个版本协议,连接过程很简单就只有一个get请求,不支持请求头而且每次发送内容都需要进行tcp连接和关闭非常浪费性能,由于没有协议头所以只能传输一种内容纯文本

  • HTTP1.0

    1996年出现了http1.0并且一直沿用到至今还在有人使用,

    1、相对于0.9版本新增了缓存策略(1.0版本的缓存策略在精准命中上还有一定问题)

    2、在请求方式上新增了POSTHEAD方式并且由此开始支持长连接方式(不过在此时长连接还需要手动设置)

    3、传输内容上也得到一定的改善支持了其他格式内容例如图像、视频、二进制文件等可以说是一个大的跨越

  • HTTP1.1

    1997年出现了http1.1也是现在用的比较多的一个版本,

    1、实现了自动tcp长连接,在请求方式上也新增了几种PUT、DELETE、CONNECT、OPTIONS等,并且解决了1.0版本缓存策略不够精准问题

    2、在请求头引入了range实现了断点续传的基础

    3、虽然在1.1版本支持了自动长连接但是也有队头阻塞的问题,同时发起了多个tcp请求在复用的连接的情况下服务端需要按照顺序处理请求并且一个一个返回,如果前面请求特别慢那么后面的请求都要排队,在客户端通常的解决方式是减少http请求或者开启多个持久连接通道这个问题在http2.0版本得到了很好的解决,

    4、最后值得一提的是1.1版本提供了host域,现如今一个服务器可以支持多个虚拟主机,这样就可以通过host域来指定具体访问那个站点

  • HTTP2.0

    2015年出现了http2.0实现了以下几个功能

    1、多路复用允许http2.0同时发起多个请求(在1.1版本中针对同域名的请求发起数量是有限制),并且提供了帧的概念所谓帧就是说把协议内容分成最小的单位去发送(甚至乱序发送),并且不需要在依赖多个tcp连接,还可以区分优先级,最后在另一端通过头部的标识把他们重新组合起来,这样就有效的避免了头部阻塞的问题,而且2.0版本连接都是持久化连接主要体现在针对同一个域名只需要发起一个连接即可

    2、头部压缩因为http协议本身是没有状态的每次的状态都是在请求或者响应中发送这样势必会造成带宽的浪费和速度的影响,2.0版本引入了压缩机制将发送的信息通过压缩手段类似gzip、compress等压缩之后发送,之后前后端会维护一张头信息表所有字段都会存入表中,再次发送的时候使用表中对应的索引即可,如果只发送索引号那么速度上肯定是有一定的提升

    3、二进制分帧在应用层http2.0和传输层tcpudp之前增加了分层,主要目的是在不改变http现有的情况下改进传输性能,实现低延迟高吞吐量,在二进制分帧层上所有消息都被分为更小的消息和帧,并采用二进制格式编码其中http1.x首部信息会被封装在header帧中,对应的request body会被封装在 Data帧中

    4、服务端推送是在客户端进行一个请求之后,发送多个响应可以实现缓存的功能,最常见的就是当请求一个index.html可以把其中用到的资源一起响应回来省的客户端在去请求,这样的方式在一定程度上可以提升页面加载速度

    常见网路中的攻击方式

  • CSRF跨站点请求伪造攻击介绍

    csrf攻击实际就是攻击者盗用你的身份利用你的身份名义发送恶意请求,而达到一些目的例如,以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。

  • 接下来我们开始实操模拟一下csrf攻击在前期我会直接把代码贴出来之后在描述流程

    1.创建一个index.html用来做登录页面

    <!DOCTYPE html>
    <html lang="en">
     
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title></title>
    </head>
     
    <body>
      <p id="login">登录</p>
      <script>
          login.onclick = function (){
            fetch('http://localhost:3001/auth').then(res => {
                //登录成功访问首页
                window.location.href = 'http://localhost:3001/home'
                return res.json()
            })
          }
      </script>
    </body>
     
    

    2.创建一个登录成功页面

    <!DOCTYPE html>
    <html lang="en"><head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head><body>
        <div>
            首页 点赞数量<span id="value">0</span>
            <button id="btn">点击</button>
        </div>
    </body>
    <script>
        window.onload = function () {
          //刷新之后获取最新count
            fetch('http://localhost:3001/getCount').then(res => {
                return res.json()
            }).then(res => {
                value.innerHTML = res
            })
        }
    ​
        btn.onclick = function () {
            //添加点赞量
            fetch('http://localhost:3001/addNum', {
                method: 'post',
                body: JSON.stringify({ value: value.innerHTML })
            }
            ).then(res => res.json()).then(res => {
                value.innerHTML = res
            })
        }
    </script></html>
    

    3.登录失败页面

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <h2>登录失败</h2>
        <button id="back">返回登录页面</button>
    </body>
    <script>
        back.onclick = function(){
            window.location.href = 'http://localhost:3001/login'
        }
    </script>
    </html>
    

    4.csrf攻击页面

    <!DOCTYPE html>
    <html lang="en"><head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head><body>
    ​
      <div class="wrapper">
        我是csrf攻击页面<span id="value"></span>
    ​
        <form action="http://localhost:3001/addNum" method='post' target="csrf">
          <input type="text" name="value">
          <input type="submit" value="改变点赞数量">
        </form>
    ​
        <iframe src="" frameborder="0" name="csrf" style="display:none"></iframe>
    ​
      </div>
    </body>
    <script></script></html>
    

    5.模拟一下server

    const http = require('http')
    const fs = require('fs')
    let count = 0
    http.createServer((req, res) => {
        const { url, headers } = req
        const { cookie = '' } = headers
        if (url === '/auth') {
            //登录设置token接口
            res.writeHead(200, {
                'Set-Cookie': 'userId=123',
                'Content-Type': 'text/plain'
            });
            res.end('ok')
        } else if (url === '/getCount') {
            //获取当前count
            res.end(count + '')
        }
        else if (url === '/addNum') {
            //添加count
            if (cookie.indexOf('userId=123') !== -1) {
                var body = '';
                req.on('data', function (chunk) {
                    body += chunk;   //读取请求体
                })
                req.on('end', function () {
                    let data = {}
                    if (body.indexOf('=') === -1) {
                        data = JSON.parse(body)
                        data.value++
                    } else {
                        let obj = {}
                        let res = body.split('=')
                        obj[res[0]] = res[1]
                        data = obj
                    }
                    count = data.value
                    res.end(count + '')
                })
            } else {
                res.end('fail')
            }
        } else if (url === '/home') {
            //进入首页
            if (cookie.indexOf('userId=123') !== -1) {
                const html = fs.readFileSync('./loginSuccess.html')
                res.end(html)
            } else {
                const html = fs.readFileSync('./fail.html')
                res.end(html)
            }
        } else if (url === '/login') {
            //登录页
            const html = fs.readFileSync('./index.html')
            res.end(html)
        }
    }).listen(3001)
    ​
    

    6.最后需要一个服务启动我们的csrf攻击页面

    const express = require('express')
    const app = express()
    const fs = require('fs')
    const chalk = require('chalk')
    ​
    app.use('/', (req, res, next) => {
        const html = fs.readFileSync('./diaoyu.html')
        res.end(html)
    });
    ​
    app.listen(3002, () => console.log(chalk.red('钓鱼网站启动中')))
    
  • 我们来看一下具体操作过程,大概流程是这样的

    1、我们先将3001端口下的首页点赞量更新到8

    2、之后我们进入csrf页面由3002端口启动然后更改点赞量到20

    3、这时候我们在回到首页一刷新就会看到点赞量被攻击者更改到了20

  • 可以看出csrf攻击主要利用了我们的登录态,来进行攻击而达到一些目的,来看一下有哪些防范手段

    1、禁止第三方网站带Cookie设置SameSite属性但是有兼容性问题(目前只有 chrome 和 opera 支持该属性。)

    2、header首部字段referer可以用来判断访问来的来源做防范,但是注意https的情况下referer字段是不发送的

    3、使用验证码 人机图形验证码+短信

    4、使用token在后端设置一个随机的数字或者字符串作为token设置到cookie中之后存储到客户端,当进行提交的时候传输到后端进行匹配如果比配成功证明是合法校验

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

    跨站脚本攻击有可能造成以下影响

    1、利用虚假输入表单骗取用户个人信息

    2、利用脚本窃取用户的Cookie值,被害者在不知情的情况下,帮助攻击者发送恶意请求。

    3、显示伪造的文章或图片。

  • XSS攻击分类

    1、反射型 - url参数直接注入,例如在http://localhost:3001/?data=<script>alert(1)</script>会在url中注入一些参数

    2、存储型 - 存储到数据库之后在读取的时候注入

  • XSS的防范手段还是比较多的

    1、可以创建一个沙箱环境筛选一下特殊字符类似<>这种

    2、防止js读取cookie可以设置cookiehttpOnly属性

    3、设置内容安全策略 (CSP, Content Security Policy) 这是一个附加的安全层,用于帮助检测和缓解某 些类型的攻击,包括跨站脚本 (XSS) 和数据注入等攻击。可以在服务端设置只允许加载本站资源、只允许加载https协议图片等

  • csp几种使用情况

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

结语

本次网络相关的总结到这里就告一段落了,总结起来也是对自己的过往知识一个查漏补缺的过程,路过的大牛如果觉得有用留个赞吧😁