URL:
HTTP 是客户端和服务器间传输数据的应用层协议 :
- 连接建立:基于 TCP,可建立短连接(HTTP/1.0 常见,请求后断开 )或持久连接(HTTP/1.1 常见,可多次请求 )。
- 请求发送:客户端发请求,含请求行(方法、URL、版本 )、请求头(客户端等信息 )、请求体(部分方法有 )。
- 服务器处理:解析请求,找对应资源或程序,生成响应。
- 响应返回:服务器回送响应,有状态行(版本、状态码、消息 )、响应头(服务器等信息 )、响应体(请求数据 )。
- 连接后续:持久连接可继续请求,短连接响应后断开 。它是无状态协议,靠 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:
#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:
#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 协议内容都是按照文本的方式明文传输的,这就导致在传输过程中出现一些被篡改的情况。
概念准备
- 什么是 “加密”
加密就是把明文 (要传输的信息) 进行一系列变换,生成密文。
解密就是把密文再进行一系列变换,还原成明文。
在这个加密和解密的过程中,往往需要一个或者多个中间的数据,辅助进行这个过程,这样的数据称为密钥
关于加密的方法有两种一种对称加密一种非对称加密:
只使用对称加密:
在只使用对称加密的方案中,客户端与服务器之间的通信依赖同一秘钥。客户端将明文请求通过秘钥加密成密文请求后发送给服务器,服务器收到密文请求后,再使用相同秘钥将其解密为明文请求。然而,若黑客入侵路由器,就可能截获请求内容,一旦秘钥在传输过程中被窃取,黑客便能利用该秘钥轻松解密密文请求,获取原始明文信息,导致通信内容泄露,这体现了仅使用对称加密在网络通信安全方面存在的风险与隐患。
只使用非对称加密:
在只使用非对称加密的方案中,服务器拥有公钥 P 和私钥 P' 。服务器先将公钥 P 发送给客户端,此时客户端到服务器(client -> server)的数据安全暂时得以保证。客户端使用公钥 P 对数据进行加密(加密过程标记为 XXXXXX )后发送。但存在风险,若黑客截获通信内容,虽然无法用公钥 P 解密数据,不过若黑客设法替换服务器发送的公钥,客户端就会使用被替换的公钥加密数据,黑客便能利用对应的私钥解密获取信息,从而威胁数据安全。
方案 3 - 双方都使用非对称加密
- 服务端拥有公钥 S 与对应的私钥 S',客户端拥有公钥 C 与对应的私钥 C'
- 客户和服务端交换公钥
- 客户端给服务端发信息:先用 S 对数据加密,再发送,只能由服务器解密,因为只有服务器有私钥 S'
- 服务端给客户端发信息:先用 C 对数据加密,在发送,只能由客户端解密,因为只有客户端有私钥 C'
这样貌似也行啊,但是
- 效率太低
- 依旧有安全问题
方案四-非对称加密+对称加密
也不可行,还是存在安全问题!
方案 5 - 非对称加密 + 对称加密 + 证书认证
在客户端和服务器刚一建立连接的时候,服务器给客户端返回一个证书,证书包含了之前服务端的公钥,也包含了网站的身份信息。证书就和身份证一样!
HTTPS 工作过程中涉及到的密钥有三组。
第一组 (非对称加密): 用于校验证书是否被篡改。服务器持有私钥 (私钥在形成 CSR 文件与申请证书时获得), 客户端持有公钥 (操作系统包含了可信任的 CA 认证机构有哪些,同时持有对应的公钥). 服务器在客户端请求是,返回携带签名的证书。客户端通过这个公钥进行证书验证,保证证书的合法性,进一步保证证书中携带的服务端公钥权威性。
第二组 (非对称加密): 用于协商生成对称加密的密钥。客户端用收到的 CA 证书中的公钥 (是可被信任的) 给随机生成的对称加密的密钥加密,传输给服务器,服务器通过私钥解密获取到对称加密密钥.
第三组 (对称加密): 客户端和服务器后续传输的数据都通过这个对称密钥加密解密.
数据摘要 && 数据指纹
- 数字指纹 (数据摘要),其基本原理是利用单向散列函数 (Hash 函数) 对信息进行运算,生成一串固定长度的数字摘要。数字指纹并不是一种加密机制,但可以用来判断数据有没有被窜改。
- 摘要常见算法:有 MD5、SHA1、SHA256、SHA512 等,算法把无限的映射成有限,因此可能会有碰撞(两个不同的信息,算出的摘要相同,但是概率非常低)。
- 摘要特征:和加密算法的区别是,摘要严格意义不是加密,因为没有解密,只不过从摘要很难反推原信息,通常用来进行数据对比。
数字签名
- 摘要经过加密,就得到数字签名