HTTP/HTTPS

URL:

HTTP 是客户端和服务器间传输数据的应用层协议 :

  1. 连接建立:基于 TCP,可建立短连接(HTTP/1.0 常见,请求后断开 )或持久连接(HTTP/1.1 常见,可多次请求 )。
  2. 请求发送:客户端发请求,含请求行(方法、URL、版本 )、请求头(客户端等信息 )、请求体(部分方法有 )。
  3. 服务器处理:解析请求,找对应资源或程序,生成响应。
  4. 响应返回:服务器回送响应,有状态行(版本、状态码、消息 )、响应头(服务器等信息 )、响应体(请求数据 )。
  5. 连接后续:持久连接可继续请求,短连接响应后断开 。它是无状态协议,靠 Cookie 等技术维持状态 。

HTTP的方法:其中最常用的就是GET和POST,两者的区别就是GET是在URL显示提交参数,而POST是在正文里面提参。

下面是HTTP的格式:

最常见的状态码,比如 200 (OK), 404 (Not Found), 403 (Forbidden), 302 (Redirect, 重定向), 504 (Bad Gateway)

HTTP常见Header

Content-type:数据类型 text、html等

Content-length:Body的长度

Host:客户端告知服务器,所请求的资源是在哪个主机的那个端口上

User-agent:声明用户的操作系统和浏览器版本信息

Refer:当前页面是从那个页面跳转过来的

Location:搭配3xx状态码使用,告诉客户端接下来要去那里访问;例如原网址搬家了,接下来要去那看信息

Cookie:用于在客户端存储少量信息。通常用于实现会话功能;

关于cookie文件看下图介绍:

cookie存放了用户的信息包括登录密码,这一点也是http不安全的地方!从而会有黑客做假网站,诱导用户登录然后提取网站cookie,导致用户个人信息被盗取!

用户发起登录后,网站给会创建一个session会话,会话中会有session id网站会把id交给浏览器。从而拥有id以后登录这个网页端时候,会检查这个id如果存在就会免登录!

使用 HTTP 需注意:

  • 安全:明文传数据易被窃听篡改,缺身份验证权限控制,敏感交互用 HTTPS 。
  • 状态:无状态,借助 Cookie、Session 等维持状态 。
  • 性能:连接效率低,可复用 TCP 连接;关注协议版本差异,新的性能优 。
  • 代理:选可靠服务商,关注速度稳定、安全隐私,注意身份验证等 。
  • 其他:防 HTTP 劫持,留意状态码排查问题 。

下面是模拟HTTP:httpserver.hpp:

cpp 复制代码
#include "Log.hpp"
#include <iostream>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "Socket.hpp"
#include <fstream>
#include <string>
#include <vector>
#include<sstream>
#include <unordered_map>
using namespace std;
static const uint16_t default_port = 8080;
const string wwwroot = "./wwwroot"; // web根目录
const string sep="\r\n";
const string homepage="index.html";
class HttpServer;
class ThreadData
{
public:
    ThreadData(int fd /*,HttpServer* svr*/) : sockfd(fd) {}

public:
    int sockfd;
    // HttpServer* httpsvr;
};
class Request
{
public:
    void DeSerialize(string &req)
    {
        while (true)
        {
            std::size_t pos = req.find(sep);
            if (pos == std::string::npos)
                break;
            std::string temp = req.substr(0, pos);
            if (!temp.empty())
                req_header.push_back(temp);
            // req.erase(0, pos + sep.size());
            req.erase((size_t)0, (size_t)pos + sep.size());
        }
        text=req;
    }

    void parse()
    {
        stringstream ss(req_header[0]);
        ss>>method>>url>>http_verison;
        file_path=wwwroot;
        if(url=="/"||url=="/index.html")
        {
            file_path+="/";
            file_path+=homepage;
        }else{
            file_path+=url;
        }
        auto pos=file_path.rfind(".");
        if(pos==string::npos) suffix=".html";
        else suffix=file_path.substr(pos);
    }
    
    void Debug()
    {
        for(auto&line:req_header)
        {
            cout<<line<<"/n/n";

        }
        cout<<"method:"<<method<<endl;
        cout<<"url:"<<url<<endl;
        cout<<"http_verison:"<<http_verison;

        cout<<text<<endl;
    }

public:
    vector<string> req_header;
    string text;

    string method;
    string url;
    string http_verison;

    string file_path;

    string suffix;
};
class HttpServer
{
public:
    HttpServer(uint16_t port = default_port) : _port(port) 
    {
        content_type.insert({".html","text/html"});
        content_type.insert({".png","image/png"});
    }

    bool Start()
    {
        _listen_sockfd_.Socket();
        _listen_sockfd_.Bind(_port);
        _listen_sockfd_.Listen();

        for (;;)
        {
            std::string clientIp;
            uint16_t clientPort;
            int sockfd = _listen_sockfd_.Accept(&clientIp, &clientPort);
            if (sockfd < 0)
            {
                continue;
            }

            ThreadData *td = new ThreadData(sockfd /*,this*/);
            // td->sockfd = sockfd;
            pthread_t tid;
            if (pthread_create(&tid, nullptr, ThreadRun, td) != 0)
            {
                // 处理线程创建失败的情况
                std::cerr << "Failed to create thread" << std::endl;
                delete td;
                close(sockfd);
            }
        }

        return true;
    }
    static string ReadHtmlContent(const string &htmlpath)
    {
        // 这里就是打开文件的正常操作
        ifstream in(htmlpath,ios::binary);
        if (!in.is_open())
            return "404";
        in.seekg(0,ios_base::end);//将文件指针移动到文件末尾(ios_base::end )
        auto len=in.tellg();//用 tellg 获取文件指针位置
        in.seekg(0,std::ios_base::beg);//将文件指针移回文件开头(std::ios_base::beg )
        string content;
        content.resize(len);

        in.read((char*)content.c_str(),content.size());//把文件内容读到content里面
        // string line;
        // string content;
        // while (getline(in, line))
        // {
        //     content += line;
        // }
        in.close();

        return content;
    }
    static string suffixToDesc(const string& suffix)
    {
        auto iter=content_type.find(suffix);
        if(iter==content_type.end())return ".html";
        else return content_type[suffix];

    }
    static void HnaderHttp(int fd)
    {
        char buffer[4096];
        ssize_t n = recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer;
            Request req;
            string bufferStr(buffer);//就nm你事多!
            req.DeSerialize(bufferStr);
            req.parse();
            req.Debug();

            // 目录
            // url="/a/b/c";
            // string path = wwwroot;
            // path += url;

            // 返回响应过程
            string text = ReadHtmlContent(req.file_path);          // 这是响应内容
            string response_line = "HTTP/1.0 200 OK\r\n"; // 状态行
            string response_header = "content_length:";   // 响应报头
            response_header += to_string(text.size());
            response_header += "\r\n";  // 每一行结束后都有换行
            response_header+="content-type";
            // string suffixToDesc=suffixToDesc(req.suffix);
            response_header+=suffixToDesc(req.suffix);
            response_header+= "\r\n"; // 这句是因为http里面响应报头和响应内容之间会有一个空白行!
            response_header+="Set cookie: name:hush11&&passwd:123456";
            response_header+= "\r\n";

            string blank_line = "\r\n";
            // 现在开始把上面哪些整合在一起
            string response = response_line;
            response += response_header;
            response += blank_line;
            response+=text;
            
            send(fd, response.c_str(), response.size(), 0);
        }

        close(fd);
    }

    static void *ThreadRun(void *args)
    {
        ThreadData *td = static_cast<ThreadData *>(args);
        pthread_detach(pthread_self());
        HnaderHttp(td->sockfd);
        // char buffer[4096];
        // ssize_t n = recv(td->sockfd, buffer, sizeof(buffer) - 1, 0);
        // if (n > 0) {
        //     buffer[n] = 0;
        //     std::cout << buffer;
        // }

        // close(td->sockfd);
        // delete td; // 释放动态分配的内存
        return nullptr;
    }

    ~HttpServer() {}

private:
    Sock _listen_sockfd_;
    uint16_t _port;
    static unordered_map<string,string> content_type;
    // 使用static关键字能让代码更加灵活和高效,特别是在需要共享数据或者实现独立于对象状态的功能时。
};

httpsever.cc:

cpp 复制代码
#include<iostream>
#include"httpServer.hpp"
#include"Log.hpp"
#include"Socket.hpp"
#include<memory>
using namespace std;

int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        exit(0);
    }
    uint16_t port=stoi(argv[1]);

    unique_ptr<HttpServer> sur(new HttpServer(port));
    sur->Start();


    return 0;
}

HTTPS 协议原理

HTTPS 是什么

HTTPS 也是一个应用层协议,是在 HTTP 协议的基础上引入了一个加密层。

HTTP 协议内容都是按照文本的方式明文传输的,这就导致在传输过程中出现一些被篡改的情况。

概念准备
  1. 什么是 "加密"
    加密就是把明文 (要传输的信息) 进行一系列变换,生成密文。
    解密就是把密文再进行一系列变换,还原成明文。
    在这个加密和解密的过程中,往往需要一个或者多个中间的数据,辅助进行这个过程,这样的数据称为密钥

关于加密的方法有两种一种对称加密一种非对称加密:

只使用对称加密:

在只使用对称加密的方案中,客户端与服务器之间的通信依赖同一秘钥。客户端将明文请求通过秘钥加密成密文请求后发送给服务器,服务器收到密文请求后,再使用相同秘钥将其解密为明文请求。然而,若黑客入侵路由器,就可能截获请求内容,一旦秘钥在传输过程中被窃取,黑客便能利用该秘钥轻松解密密文请求,获取原始明文信息,导致通信内容泄露,这体现了仅使用对称加密在网络通信安全方面存在的风险与隐患。

只使用非对称加密:

在只使用非对称加密的方案中,服务器拥有公钥 P 和私钥 P' 。服务器先将公钥 P 发送给客户端,此时客户端到服务器(client -> server)的数据安全暂时得以保证。客户端使用公钥 P 对数据进行加密(加密过程标记为 XXXXXX )后发送。但存在风险,若黑客截获通信内容,虽然无法用公钥 P 解密数据,不过若黑客设法替换服务器发送的公钥,客户端就会使用被替换的公钥加密数据,黑客便能利用对应的私钥解密获取信息,从而威胁数据安全。

方案 3 - 双方都使用非对称加密

  1. 服务端拥有公钥 S 与对应的私钥 S',客户端拥有公钥 C 与对应的私钥 C'
  2. 客户和服务端交换公钥
  3. 客户端给服务端发信息:先用 S 对数据加密,再发送,只能由服务器解密,因为只有服务器有私钥 S'
  4. 服务端给客户端发信息:先用 C 对数据加密,在发送,只能由客户端解密,因为只有客户端有私钥 C'

这样貌似也行啊,但是

  • 效率太低
  • 依旧有安全问题

方案四-非对称加密+对称加密

也不可行,还是存在安全问题!

方案 5 - 非对称加密 + 对称加密 + 证书认证

在客户端和服务器刚一建立连接的时候,服务器给客户端返回一个证书,证书包含了之前服务端的公钥,也包含了网站的身份信息。证书就和身份证一样!

HTTPS 工作过程中涉及到的密钥有三组。

第一组 (非对称加密): 用于校验证书是否被篡改。服务器持有私钥 (私钥在形成 CSR 文件与申请证书时获得), 客户端持有公钥 (操作系统包含了可信任的 CA 认证机构有哪些,同时持有对应的公钥). 服务器在客户端请求是,返回携带签名的证书。客户端通过这个公钥进行证书验证,保证证书的合法性,进一步保证证书中携带的服务端公钥权威性。

第二组 (非对称加密): 用于协商生成对称加密的密钥。客户端用收到的 CA 证书中的公钥 (是可被信任的) 给随机生成的对称加密的密钥加密,传输给服务器,服务器通过私钥解密获取到对称加密密钥.

第三组 (对称加密): 客户端和服务器后续传输的数据都通过这个对称密钥加密解密.

数据摘要 && 数据指纹

  • 数字指纹 (数据摘要),其基本原理是利用单向散列函数 (Hash 函数) 对信息进行运算,生成一串固定长度的数字摘要。数字指纹并不是一种加密机制,但可以用来判断数据有没有被窜改。
  • 摘要常见算法:有 MD5、SHA1、SHA256、SHA512 等,算法把无限的映射成有限,因此可能会有碰撞(两个不同的信息,算出的摘要相同,但是概率非常低)。
  • 摘要特征:和加密算法的区别是,摘要严格意义不是加密,因为没有解密,只不过从摘要很难反推原信息,通常用来进行数据对比。

数字签名

  • 摘要经过加密,就得到数字签名
相关推荐
时序数据说2 小时前
IoTDB集群部署中的网络、存储与负载配置优化
大数据·网络·分布式·时序数据库·iotdb
嵌入式在学无敌大神3 小时前
Linux网络编程:TCP多进程/多线程并发服务器详解
linux·服务器·网络
Jackilina_Stone4 小时前
【网工第6版】第6章 网络安全③
网络·软考·网工
christine-rr5 小时前
【25软考网工】第四章(3)MAC层访问控制、移动Ad Hoc网络
运维·网络·软考·考试
时迁2476 小时前
【计算机网络】面试常考——GET 和 POST 的区别
计算机网络·http·get·post
自由鬼8 小时前
深海科技服务博客简介
运维·网络·人工智能·程序人生·安全威胁分析·个人开发·数据安全
m0_564264189 小时前
【尝试解决】无法连接到xxx:L2TP连接尝试失败,因为安全层在初始化与远程计算机的协商时遇到一个处理错误。
网络·经验分享·内网穿透·vpn·局域网·ipsec·l2tp
努力也学不会java9 小时前
【网络原理】 《TCP/IP 协议深度剖析:从网络基础到协议核心》
网络·网络协议·tcp/ip·安全·php
Vicssic9 小时前
【Linux 网络】网络工具ifconfig和iproute/iproute2工具详解
linux·运维·网络