Linux --- 應用層 | HTTP | HTTPS

前言

前面寫的TCP/UDP客戶端在訪問服務端的時候,需要輸入ip地址和端口號才可以訪問, 但在現實中,我們訪問一個網站是直接輸入的一個域名,而不是使用的ip地址+端口號。

比如在訪問百度 https://www.baidu.com/的時候, 是使用的域名,瀏覽器會把域名進行解析成為ip地址+端口號。

我們ping一下百度的域名,會得到來自百度的一個回復,然后我們可以訪問這個ip地址就會得到百度的主頁面。

上面在進行網絡請求的時候,并沒有輸入端口號,在輸入ip地址之后,直接進入到了百度的主頁面。

把這里復制粘貼。百度一下,你就知道 其實是有一個http前綴的,我們訪問其他網頁,也會有http或者https前綴的。輸入網址就算不輸入http或https,瀏覽器會把這兩個協議進行默認拼接。一般像這種知名的服務器會把端口號給固定下來。這種端口號是不能隨意修改的。所以我們在訪問百度的時候,把ip地址輸入進去,可以直接訪問到百度的主頁面,其實就是訪問的39.156.66.14:80。

認識URL

我們看到了好的文章,然后把鏈接(https://blog.csdn.net/weixin_73888239/category_12238116.html )復制下來分享給朋友,像這種鏈接就叫做URL 統一資源定位符。在全網當中,只要有這個URL,就可以訪問這個網頁。每一個字符串在全網當中,都是唯一的。在網絡上我們所看到的一些圖片,音樂,視頻,直播等資源都可以用唯一的一個字符串標識,并且可以獲取到,只要知道url就可以訪問這些資源。

url的格式一般為下圖所示

urlencode和urldecode

urlencode和urldecode是用于處理URL編碼和解碼的兩個相關的操作,通常用于將特殊字符轉換為URL安全的形式,以及將已編碼的URL轉換回原始形式。

urlencode 用于將字符串轉換為url安全的格式,將特殊字符轉換為其對應的百分比編碼形式。

urldecode 用于解碼已經被url編碼的字符串,將百分比編碼形式還原為原始字符

像 / ? : 等這樣的字符, 已經被url當做特殊意義理解了. 因此這些字符不能隨意出現.比如, 某個參數中需要帶有這些特殊字符, 就必須先對特殊字符進行轉義.
轉義的規則如下:
將需要轉碼的字符轉為16進制,然后從右到左,取4位(不足4位直接處理),每2位做一位,前面加上%,編碼成%XY格式

"+" 被轉義成了 "%2B",urldecode就是urlencode的逆過程;

HTTP協議格式

我們平時上網的行為其實就兩種

  1. 從服務器端拿下來資源數據 --- get方法 (可以通過 表單 的方法展示出來)表單收集用戶數據(表單是要被提交的),并把用戶數據推送給服務器(表單中的數據,會被轉成http request的一部分)
  2. 把客戶端的數據提交到服務器 --- post方法get方法都可以

get方法傳參通過url傳參,會回顯輸入的私密信息,不夠私密

post方法通過正文提交傳參,不會回顯的輸出信息.一般私密性是有保證的

這里的私密性不是安全性,數據只有經過加密和解密才會安全。


http request中,是有一個請求行,請求報頭,請求正文組成,在請求行中,有請求方法(GET,POST),URL,HTTP Version組成,這三個之間以空格作為分隔符。中間部分是請求報頭,都是以KV的形式存在,最后是請求正文,在請求正文和請求報頭之間,存在一個空行,這是為了區分請求報頭和請求正文而存在的。在讀取http request的時候,按照行讀取,這樣就可以將報文和有效載荷成功的分離,不會讀到不屬于自己的數據。

在HTTP請求的時候,會先將我們所輸入的域名進行解析,然后去訪問該內容,客戶端在與服務端建立TCP連接,通過三次握手確保雙方可以進行可靠的通信。然后構建HTTP請求消息。客戶端構建一個HTTP請求消息,其中包括請求行,請求報頭,空行,請求正文。請求消息發送到服務器,服務器會進行處理并構建響應消息(狀態行,響應報頭,響應正文),然后將響應消息發送給客戶端,并關閉連接。

可以使用telnet工具來完成一次http的請求和響應。

首行: [方法] + [url] + [版本]
Header: 請求的屬性, 冒號分割的鍵值對;每組屬性之間使用\n分隔;遇到空行表示Header部分結束
Body: 空行后面的內容都是Body. Body允許為空字符串. 如果Body存在, 則在Header中會有一個
Content-Length屬性來標識Body的長度;

telnet www.baidu.com 80
在按ctrl + ]
回車// http請求
GET / HTTP/1.1 // 請求行// http響應
HTTP/1.1 200 OK // 響應狀態行中存在 http的版本,狀態碼,狀態碼描述,跟請求一樣,都是以空格作為分隔符
Accept-Ranges: bytes
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 9508
Content-Type: text/html
Date: Thu, 29 Feb 2024 07:49:00 GMT
P3p: CP=" OTI DSP COR IVA OUR IND COM "
P3p: CP=" OTI DSP COR IVA OUR IND COM "
Pragma: no-cache
Server: BWS/1.1
Set-Cookie: BAIDUID=BDADB3AA66EC6897715119E26C4CF88A:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BIDUPSID=BDADB3AA66EC6897715119E26C4CF88A; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1709192940; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BAIDUID=BDADB3AA66EC689787381350CD38E8C2:FG=1; max-age=31536000; expires=Fri, 28-Feb-25 07:49:00 GMT; domain=.baidu.com; path=/; version=1; comment=bd
Traceid: 1709192940051597876211264035507514709484
Vary: Accept-Encoding
X-Ua-Compatible: IE=Edge,chrome=1
X-Xss-Protection: 1;mode=blockHTML/CSS/JS 頁面。

首行: [版本號] + [狀態碼] + [狀態碼解釋]
Header: 請求的屬性, 冒號分割的鍵值對;每組屬性之間使用\n分隔;遇到空行表示Header部分結束
Body: 空行后面的內容都是Body. Body允許為空字符串. 如果Body存在, 則在Header中會有一個
Content-Length屬性來標識Body的長度; 如果服務器返回了一個html頁面, 那么html頁面內容就是在body中.


telnet是自己構建的請求。可以用費德勒這個軟件進行抓包。

HttpDone

其實我們也可以自己寫一個簡單的http。

#pragma once#include <iostream>
#include <string>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>#include "log1.hpp"
#include "Socket.hpp"extern Log lg;class HttpServer;class ThreadData {
public:ThreadData(int sockfd):_sockfd(sockfd){}~ThreadData(){}
public:public:HttpServer *serv;int _sockfd;
}; // 存儲線程數據const std::string defaultip = "0.0.0.0";
class HttpServer {
public:HttpServer(uint16_t port):_port(port),_ip(defaultip){}~HttpServer(){}
public:void Init(){_listensock = sock.Socket();lg(Info, "socket success");sock.Bind(_listensock, _port);lg(Info, "Bind success");sock.Listen(_listensock);lg(Info, "Listen success");}static void *ThreadRun(void *args){pthread_detach(pthread_self()); ThreadData* td = static_cast<ThreadData*>(args);char buf[1024];while (true){ssize_t n = read(td->_sockfd, buf, sizeof(buf) - 1);if (n > 0){buf[n] = 0;std::cout << buf << std::endl;}}}bool Start(){while (true){std::string clientip;uint16_t clientport;int sockfd = sock.Accept(_listensock, clientip, clientport);lg(Info, "accept success");ThreadData *td = new ThreadData(sockfd);pthread_t tid;pthread_create(&tid, nullptr, ThreadRun, td);}}
private:int _listensock;uint16_t _port;std::string _ip;Sock sock;
};
#include "HttpServer.hpp"
#include <memory>int main(int argc, char *argv[])
{   if (argc != 2){exit(1);}uint16_t port = std::stoi(argv[1]);std::unique_ptr<HttpServer> serv(new HttpServer(port));serv->Init();serv->Start();return 0;
}

#pragma once#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log1.hpp"Log lg;const int backlog = 10;enum
{SocketErr = 2,BindErr,ListenErr,
};class Sock
{
public:Sock(){}~Sock(){}int Socket(){_listensocket = socket(AF_INET, SOCK_STREAM, 0);if (_listensocket < 0){lg(Fatal, "socker error, %s: %d", strerror(errno), errno);exit(SocketErr);}}void Bind(int listensock, uint16_t port){struct sockaddr_in serv;bzero(&serv, sizeof(serv));serv.sin_family = AF_INET;serv.sin_port = htons(port);serv.sin_addr.s_addr = INADDR_ANY;if (bind(listensock, (const sockaddr*)&serv, sizeof(serv)) < 0){lg(Fatal, "bind error, %s: %d", strerror(errno), errno);exit(BindErr);}}void Listen(int listensock){if (listen(listensock, backlog) < 0){lg(Fatal, "listen error, %s: %d", strerror(errno), errno); exit(ListenErr);}}int Accept(int listensock, std::string& ip, uint16_t& port){struct sockaddr_in serv;bzero(&serv, sizeof(serv));socklen_t len = sizeof(serv);int sockfd = accept(listensock, (struct sockaddr*)&serv, &len);if (sockfd < 0){lg(Warning, "accept error, %s: %d", strerror(errno), errno);return -1;}char ipstr[64];inet_ntop(AF_INET, &serv.sin_addr.s_addr, ipstr, sizeof(ipstr));ip = ipstr;port = ntohs(serv.sin_port);return sockfd;}bool Connect(int listensock, const std::string &ip, const uint16_t& port){struct sockaddr_in serv;bzero(&serv, sizeof(serv));serv.sin_family = AF_INET;serv.sin_port = htons(port);inet_pton(AF_INET, ip.c_str(), &serv.sin_addr.s_addr);int n = connect(listensock, (const struct sockaddr*)&serv, sizeof(serv));if (n < 0){std::cerr << "connect to " << ip << ":" << port << " error" << std::endl;return false;}return true;}void Close(int listensock){close(listensock);}int Fd(){return _listensocket;}private:int _listensocket;
};

在運行之后,讓PC端和手機端分別訪問該服務端。

[Info][2024-3-1 14:2:55] socket success[Info][2024-3-1 14:2:55] Bind success[Info][2024-3-1 14:2:55] Listen success[Info][2024-3-1 14:3:11] accept success[Info][2024-3-1 14:3:11] accept successGET / HTTP/1.1  
Host: 1.117.232.232:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6[Info][2024-3-1 14:3:41] accept success[Info][2024-3-1 14:3:41] accept successGET / HTTP/1.1 
Host: 1.117.232.232:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Linux; U; Android 14; zh-CN; 23127PN0CC Build/UKQ1.230804.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/6.9.6.501 Mobile Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7

可以看到請求報頭中都是kv結構的數據。其中有一個User-Agent,他代表的是瀏覽器的版本和用戶的操作系統。

還有其他的一些數據。

在瀏覽器上下載軟件的時候,可以直接下載PC版的安裝包,用手機瀏覽器下載軟件會直接下載手機版的安裝包,這就是通過User-Agent來判斷用戶是用的手機端還是PC端,判斷之后再給用戶推送合適的內容。


其實我們可以自己構建一個http響應,當客戶端連接服務端的時候,服務端會給客戶端一個響應。

先寫一個簡單的網頁當作響應正文

<!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><h1>LOG IN</h1></body>
</html>

在寫一個響應狀態行和響應報頭,將狀態行,響應報頭和響應正文進行拼接。發送給瀏覽器,就可以得到數據了。

static std::string ReadHtmlContent(const std::string &htmlpath){std::ifstream in(htmlpath);if (!in.is_open()){return "404";}std::string line;std::string content;while (std::getline(in, line)){content += line;}return content;}static void HandlerHttp(int sockfd)
{char buf[10240];ssize_t n = recv(sockfd, buf, sizeof(buf) - 1, 0);if (n > 0){buf[n] = 0;std::cout << buf;// 構建服務端響應消息std::string text = ReadHtmlContent("wwwroot/index.html"); // 響應正文std::string response_line = "HTTP/1.1 200 OK\r\n"; // 響應狀態行std::string response_header = "Content-Length: ";  // 響應報頭response_header += std::to_string(text.size());    // 響應報頭response_header += "\r\n";std::string blank_line = "\r\n";                   //  空行,來分割響應正文和響應報頭std::string response = response_line;response += response_header;response += blank_line;response += text;ssize_t m = send(sockfd, response.c_str(), response.size(), 0);if (m < 0){lg(Debug, "send error");}// Close the socket after sending the responseclose(sockfd);}else if (n == 0){// Connection closed by the clientclose(sockfd);}else{// Handle the receive errorlg(Debug, "recv error");close(sockfd);}
}

運行之后就會出現剛才寫的頁面,也可以在頁面中添加a標簽,進行鏈接跳轉。

http的方法

上面的請求都是get方法,我們最常用的是get和post方法。其他的方法了解一下即可。

如何把數據提交給服務器呢?我們登錄賬號的時候,會有一個登錄頁面,這個登錄頁面其實就是一個表單,通過表單將數據提交給服務器。

這是隨便找的一個登錄頁面的頁面代碼。如果上面寫的http需要數據,也可以寫一個表單頁面,然后將數據提交給服務器。

<!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><h1>LOG IN</h1><form action="" method="get">name: <input type="text" name="name" value=""><br>password: <input type="password" name="name" value=""><br><input type="submit" value="提交"></form>
</body>
</html>

表單很丑陋,哈哈,這不是我們該考慮的問題。

在我們輸入name和pwd之后,看瀏覽器中的url,

使用get方法將參數交給服務器,是通過url提交的,將參數拼接到了url的后面,來完成請求。

我們的服務端也會收到這個url。


將form中的method換成post方法之后,在將表單提交。,url中不會出現輸入的name和pwd。

在寫的服務端中查看瀏覽器的請求,可以看到使用的是post方法和數據。


可能會有人說,get方法會把數據顯示到url上,不安全;post不會顯示,相對安全。其實不是這樣,數據只有在經過加密之后,才會變得安全。get方法只能說是不夠私密,post方法私密一些。

http的狀態碼

前面寫的簡易http代碼中,瀏覽器發送請求后,服務端會進行響應,響應中的狀態碼寫的是200,代表服務端響應成功。

我們在訪問京東的時候,將url寫為 www.jd.com/a/b/c就會出現找不到的情況。這就是404(Not Found)。

最常見的狀態碼, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)

重定向

瀏覽器向服務端發送一個請求,服務端對瀏覽器做出響應,但是瀏覽器中輸入的url是不應該在被使用的url,應該使用新的地址向服務端發送請求。所以在使用老地址發送請求的時候,服務端做出的響應報頭中,會存在一個location: 新地址 的kv結構然后瀏覽器使用這個新地址,在對服務端發送請求。

重定向就是服務器指導瀏覽器訪問新地址。

std::string response_line = "HTTP/1.1 302 Found\r\n"; // 響應狀態行std::string response_header = "Content-Length: ";  // 響應報頭
response_header += std::to_string(text.size());    // 響應報頭response_header += "\r\n";
response_header += "Location: https://www.jd.com\r\n";
std::string blank_line = "\r\n";                   //  空行,來分割響應正文和響應報頭std::string response = response_line;
response += response_header;
response += blank_line;
response += text;ssize_t m = send(sockfd, response.c_str(), response.size(), 0);
if (m < 0)
{lg(Debug, "send error");
}close(sockfd);

將響應狀態行進行修改,將響應報頭中添加上location:地址 字段,就可以完成重定向了。在訪問服務端,就會跳轉到jd的頁面了。


臨時重定向(Temporary Redirect):

  1. 使用狀態碼 302 Found 或 307 Temporary Redirect 表示。
  2. 表示請求的資源暫時被移動到了其他位置。
  3. 客戶端在接收到這樣的狀態碼時,應該繼續使用原始的 URL 進行請求。
  4. 臨時重定向是暫時性的,客戶端以后可能會繼續使用原始 URL,因為重定向只是暫時的。

永久重定向 (Permanent Redirect):

  1. 使用狀態碼 301 Moved Permanently 或 308 Permanent Redirect 表示。
  2. 表示請求的資源已經永久地移到了其他位置。
  3. 客戶端在接收到這樣的狀態碼時,應該更新其鏈接并使用新的 URL 進行以后的請求。
  4. 永久重定向是持久性的,客戶端應該更新其鏈接,以便將來的請求直接發送到新的 URL 上。

http常見的header

  1. Content-Type: 數據類型(text/html等)

<img src="C:\Users\Lenovo\Pictures\1708049137066.jpg" alt="src error">

在運行之后,圖片加載不出來,瀏覽器沒有解釋出來,這是因為格式的 問題,要在響應報頭上添加上Content-Type:數據類型 這個kv結構。因為我們寫的http有點簡陋,僅僅添加上這個報頭也不能響應,瀏覽器先發送請求請求的是html的頁面,然后再次請求,請求的才是圖片,如果把Content-Type改成圖片就不能顯示頁面了,圖片也顯示不出來,所以可以添加上一個函數,用來判斷請求的是什么類型。

  1. Content-Length: Body的長度
  2. Host: 客戶端告知服務器, 所請求的資源是在哪個主機的哪個端口上;
  3. User-Agent: 聲明用戶的操作系統和瀏覽器版本信息;
  4. referer: 當前頁面是從哪個頁面跳轉過來的;
  5. location: 搭配3xx狀態碼使用, 告訴客戶端接下來要去哪里訪問;
  6. Cookie: 用于在客戶端存儲少量信息. 通常用于實現會話(session)的功能
  7. connection:keep-alive---長連接

一次連接可以被多個請求-響應復用, 在HTTP/1.1中,默認情況下是啟用了長連接的。
9. connection:close---短連接

每個請求-響應都需要建立一個新的連接,通常用于HTTP/1.0中每個連接只處理一個請求,處理完畢后即關閉連接,不保持持續的連接狀態。

會話Cookie

在瀏覽器上登錄b站,關掉瀏覽器,再次打開瀏覽器看b站,是不需要在進行登錄。

這是服務器發送到用戶瀏覽器并保存在本地的一小塊數據。瀏覽器會存儲cookie并在下次向同意服務器再發起請求時,攜帶并發送到服務器上的。通常,它用于告知服務端兩個請求是否來自同一瀏覽器,如保持用戶的登陸狀態。Cookie使基于無狀態的HTTP協議記錄穩定的狀態信息成為了可能。

將http請求中添加上了set-cookie結構,就會產出cookie文件,里面保存的就是賬號密碼,這也就是為什么我們登錄網站的時候,一段時間內再次訪問同樣網站的時候,瀏覽器和服務器相互配合,服務器自動的去認證cookie,就不需要重復登錄了。

static void HandlerHttp(int sockfd){char buf[10240];ssize_t n = recv(sockfd, buf, sizeof(buf) - 1, 0);if (n > 0){buf[n] = 0;std::cout << buf;// 構建服務端響應消息std::string text = ReadHtmlContent("wwwroot/index.html"); // 響應正文std::string response_line = "HTTP/1.0 200 OK\r\n"; // 響應狀態行// std::string response_line = "HTTP/1.1 302 Found\r\n"; // 響應狀態行std::string response_header = "Content-Length: ";  // 響應報頭response_header += std::to_string(text.size());    // 響應報頭response_header += "\r\n";response_header += "Set-Cookie: ";response_header += "123456";response_header += "\r\n";// response_header += "Location: https://www.jd.com\r\n";std::string blank_line = "\r\n";                   //  空行,來分割響應正文和響應報頭std::string response = response_line;response += response_header;response += blank_line;response += text;ssize_t m = send(sockfd, response.c_str(), response.size(), 0);if (m < 0){lg(Debug, "send error");}// Close the socket after sending the responseclose(sockfd);}else if (n == 0){// Connection closed by the clientclose(sockfd);}else{// Handle the receive errorlg(Debug, "recv error");close(sockfd);}}

cookie固然方便,但也有一些問題

  1. cookie被盜取的問題
  2. 個人信息泄露的問題

當瀏覽器發起請求的時候,會把cookie發送過去,然后服務端進行認證,認證成功之和,服務端會為我們創建一個session文件,這個文件里會記錄下來用戶登錄相關的內容,形成session文件,還會生成一個全服務器內唯一的一個session ID,這個ID是一種序列號,以session ID為session文件進行命名,將session ID返回給用戶,用戶的Cookie中存儲的就是這個session ID,往后,瀏覽器再次發送請求,Cookie中存的就是session ID,服務端進行查找就行了。假如說我訪問的是B站,B站的用戶非常多,服務端如何管理這個session ID呢? 先描述,在組織,session文件中有用戶自己的屬性,還有sessionID,所以只要以某種數據結構的形式把這些文件或ID連接起來,就能以增刪查改的形式進行管理了。

如果黑客把用戶發送的請求給截取了,那么黑客就會拿到Cookie了,黑客就可以通過Cookie訪問用戶的賬號了,但是Cookie中存儲的是session ID,個人信息是不會泄露的。各大網站都有技術人員,這種情況肯定會有解決方法。

https

HTTPS也是?個應?層協議.是在HTTP協議的基礎上引?了?個加密層.HTTP協議內容都是按照?本的?式明?傳輸的.這就導致在傳輸過程中出現?些被篡改的情況.

http是以明文的形式傳輸,不加密,不提供對數據完整性的保障,容易受到中間人攻擊。HTTPS是在HTTP的基礎上添加了安全曾(SSL),用于對數據加密解密。通過SSL協議,確保數據在傳輸過程中不被竊取或篡改。應用層中http經過SSL加密解密后,到傳輸層,傳輸層并不知道該數據是經過加密的,只有應用層才會知道。

加密解密

加密就是把明?(要傳輸的信息)進??系列變換,?成密?解密就是把密?再進??系列變換,還原成明?在這個加密和解密的過程中,往往需要?個或者多個中間的數據,輔助進?這個過程,這樣的數據稱為密鑰。假如7 ^ 5 = 010, 這個7就是名文,這個 010就是密文,中間的這個5就是密鑰。5 ^ 010 = 7,這樣就可以得到明文。

加密解密到如今已經發展成?個獨?的學科:密碼學.?密碼學的奠基?,也正是計算機科學的祖師爺之?艾倫·?席森·圖靈

因為http的內容是明?傳輸的,明?數據會經過路由器、wifi熱點、通信服務運營商、代理服務器等多個物理節點,如果信息在傳輸過程中被劫持,傳輸的內容就完全暴露了。劫持者還可以篡改傳輸的信息且不被雙?察覺,這就是中間?攻擊 ,所以我們才需要對信息進?加密.不?運營商可以劫持,其他的?客也可以?類似的?段進?劫持,來竊取??隱私信息,或者篡改內容.

在互聯網中,明文傳輸是一件非常危險的事情,HTTPS就是在HTTP的基礎上進行了加密,進一步的來保證用戶的信息安全。

常見的加密方式

對稱加密

采?單鑰密碼系統的加密?法,同?個密鑰可以同時?作信息的加密和解密,這種加密?法稱為對
稱加密,也稱為單密鑰加密,特征:加密和解密所?的密鑰是相同的。
常?對稱加密算法(了解):DES、3DES、AES、TDEA、Blowfish、RC2等
特點:算法公開、計算量?、加密速度快、加密效率?
對稱加密其實就是通過同?個"密鑰",把明?加密成密?,并且也能把密?解密成明?.
?

非對稱加密

需要兩個密鑰來進行加密和解密。 一個是公鑰,一個是私鑰。公鑰和私鑰是配對的,最大的缺點就是運算速度非常慢,比對稱加密要慢很多。

明文 由 公鑰A 加密變成密文, 密文由公鑰B進行解密變成明文,也可以反著來,通過私鑰對明文加密變成密文,通過公鑰對密文進行解密,變成明文。

常??對稱加密算法(了解):RSA,DSA,ECDSA

特點:算法強度復雜、安全性依賴于算法與密鑰但是由于其算法復雜,?使得加密解密速度沒有對
稱加密解密的速度快

數據摘要 && 數據指紋

數字指紋(數據摘要),其基本原理是利?單向散列函數(Hash函數)對信息進?運算,?成?串固定?度
的數字摘要。數字指紋并不是?種加密機制,但可以?來判斷數據有沒有被竄改。
摘要常?算法:有MD5、SHA1、SHA256、SHA512等,算法把?限的映射成有限,因此可能會有碰撞(兩個不同的信息,算出的摘要相同,但是概率?常低)
摘要特征:和加密算法的區別是,摘要嚴格意義不是加密,因為沒有解密,只不過從摘要很難反推
原信息,通常?來進?數據對?


HTTPS加密方案

方案一 - 只使用對稱加密

如果通信雙?都各?持有同?個密鑰X,且沒有別?知道,這兩?的通信安全當然是可以被保證的(除?密鑰被破解)

客戶端向服務端發送密鑰的時候,服務器能獲取密鑰,黑客也能獲取這個密鑰,那么可以讓密鑰進行加密,在發送給服務端,但是服務端并不知道對加密內容解密的密鑰是什么,所以還是要先發送密鑰,那么黑客還是可以直接獲取密鑰。這就導致了是先有雞還是先有蛋的問題。

所以方案一是不可取的。

方案二 - 只使用非對稱加密

非對稱加密有兩個密鑰,一個公鑰,一個私鑰。客戶端向服務端發送請求的時候,服務端會把公鑰發送給客戶端,此后客戶端在向服務端發送數據的時候,數據會進行加密,只有私鑰能解,只有服務器有這個私鑰。黑客就算獲得了公鑰,沒有私鑰,也不能將被公鑰加密過的數據解密。當服務端接受到客戶端的信息后,要向客戶端發起響應,這個響應是被私鑰加密過的,黑客是有公鑰的,所以黑客可以將服務端發送給客戶端的數據給解密。

所以方案二只能保證單方向的數據安全性,此方案也不可取。

方案三 - 雙方都是用非對稱加密

服務端擁有公鑰S與對應的私鑰S',客?端擁有公鑰C與對應的私鑰C', 客戶端和服務端交換公鑰。客?端給服務端發信息:先?S對數據加密,再發送,只能由服務器解密,因為只有服務器有私鑰S'服務端給客?端發信息:先?C對數據加密,在發送,只能由客?端解密,因為只有客?端有私鑰C'。這樣貌似能行,雙方協商完畢就可以保證安全性了。但是他的效率非常低。安全性問題還存在。

方案四 - ?對稱加密+對稱加密


客戶端先拿到服務端發送的公鑰S, 然后客戶端自己形成一個對稱密鑰C, 由公鑰S和密鑰C一起加密成XXX,然后發送給服務端, XXX在和私鑰S` 解密成 C,此時服務端就有了對稱密鑰。這樣就保證了數據安全,效率問題也有保證了。這個方案還是存在問題。

雖然上?已經?較接近答案了,但是依舊有安全問題
?案2,?案3,?案四都存在?個問題,如果最開始,中間?就已經開始攻擊了呢?

中間人攻擊 - 針對上面的場景

Man-in-the-MiddleAttack,簡稱“MITM攻擊"

確實,在?案2/3/4中,客?端獲取到公鑰S之后,對客?端形成的對稱秘鑰X?服務端給客?端的公鑰S進?加密,中間?即使竊取到了數據,此時中間?確實?法解出客?端形成的密鑰X,因為只有服務器有私鑰S'但是中間?的攻擊,如果在最開始握?協商的時候就進?了,那就不?定了,假設hacker已經成功成為中間? 。

  1. 服務器具有?對稱加密算法的公鑰S,私鑰S'
  2. 中間?具有?對稱加密算法的公鑰M,私鑰M'
  3. 客?端向服務器發起請求,服務器明?傳送公鑰S給客?端
  4. 中間?劫持數據報?,提取公鑰S并保存好,然后將被劫持報?中的公鑰S替換成為??的公鑰M,
    并將偽造報?發給客?端
  5. 客?端收到報?,提取公鑰M(??當然不知道公鑰被更換過了),??形成對稱秘鑰X,?公鑰M加
    密X,形成報?發送給服務器
  6. 中間?劫持后,直接???的私鑰M'進?解密,得到通信秘鑰X,再?曾經保存的服務端公鑰S加
    密后,將報?推送給服務器
  7. 服務器拿到報?,???的私鑰S'解密,得到通信秘鑰X
  8. 雙?開始采?X進?對稱加密,進?通信。但是?切都在中間?的掌握中,劫持數據,進?竊聽甚
    ?修改,都是可以的
    ?

上?的攻擊?案,同樣適?于?案2,?案3
問題本質出在哪?了呢?客?端?法確定收到的含有公鑰的數據報?,就是?標服務器發送過來的!

?

CA證書

在訪問網站的時候,可能會有這樣的情況,網站的安全證書已經過期,是否選擇相信之類的情況。其實就是CA證書到期了。

服務端在使?HTTPS前,需要向CA機構申領?份數字證書,數字證書?含有證書申請者信息、公鑰信息等。服務器把證書傳輸給瀏覽器,瀏覽器從證書?獲取公鑰就?了,證書就如?份證,證明服務端公鑰的權威性。

這個證書可以理解為是一個結構化的字符串,里面包含了以下信息:

  1. 證書發布機構
  2. 證書有效期
  3. 公鑰
  4. 證書所有者
  5. 簽名
  6. ……

需要注意的是:申請證書的時候,需要在特定平臺?成查,會同時?成?對?密鑰對?,即公鑰和私
鑰。這對密鑰對?就是?來在?絡通信中進?明?加密以及數字簽名的。其中公鑰會隨著CSR?件,?起發給CA進?權威認證,私鑰服務端??保留,?來后續進?通信(其實主要就是?來交換對稱秘鑰)

可以使用在線生成CSR和密鑰形成CSR之后,后續就是向CA進?申請認證,不過?般認證過程很繁瑣,?絡各種提供證書申請的服務商,?般真的需要,直接找平臺解決就?

方案五 - 非對稱加密 + 對稱加密 + 證書認證

在客?端和服務器剛?建?連接的時候,服務器給客?端返回?個證書,證書包含了之前服務端的公鑰,也包含了?站的?份信息.
?


客?端進?認證
當客?端獲取到這個證書之后,會對證書進?校驗(防?證書是偽造的).
判定證書的有效期是否過期
判定證書的發布機構是否受信任(操作系統中已內置的受信任的證書發布機構).
驗證證書是否被篡改:從系統中拿到該證書發布機構的公鑰,對簽名解密,得到?個hash值(稱為數據摘要),設為hash1.然后計算整個證書的hash值,設為hash2.對?hash1和hash2是否相等.如果相等,則說明證書是沒有被篡改過的。

中間?有沒有可能篡改該證書?
1. 中間?篡改了證書的明?
2. 由于他沒有CA機構的私鑰,所以?法hash之后?私鑰加密形成簽名,那么也就沒法辦法對篡改后
的證書形成匹配的簽名
3. 如果強?篡改,客?端收到該證書后會發現明?和簽名解密后的值不?致,則說明證書已被篡改,
證書不可信,從?終?向服務器傳輸信息,防?信息泄露給中間?
中間?整個掉包證書?
1. 因為中間?沒有CA私鑰,所以?法制作假的證書(為什么?)
2. 所以中間?只能向CA申請真證書,然后???申請的證書進?掉包
3. 這個確實能做到證書的整體掉包,但是別忘記,證書明?中包含了域名等服務端認證信息,如果整
體掉包,客?端依舊能夠識別出來。
4. 永遠記住:中間?沒有CA私鑰,所以對任何證書都?法進?合法修改,包括??的

完成流程

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/718505.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/718505.shtml
英文地址,請注明出處:http://en.pswp.cn/news/718505.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RocketMQ - 深入研究一下消費者是如何獲取消息處理以及進行ACK

1. 消費者組到底是個什么概念 消費者組的意思就是讓你給一組消費者起一個名字,比如有一個Topic叫“TopicOrderPaySuccess”,然后假設有庫存系統、積分系統、營銷系統、倉儲系統他們都要去消費這個Topic中的數據。 此時我們應該給這四個系統分別起一個消費組的名字,比如sto…

Linux:管道文件及相關API

目錄 前言一、管道文件1、基本概念2、匿名(無名)管道3、命名(有名)管道4、管道的特點5、思考&#xff1a;何時只能使用無名管道&#xff0c;何時又只能用有名管道&#xff1f;無名管道&#xff08;匿名管道&#xff09;適用的情況&#xff1a;有名管道&#xff08;命名管道&…

2024最新AI系統ChatGPT網站源碼, AI繪畫系統

一、前言說明 R5Ai創作系統是基于ChatGPT進行開發的Ai智能問答系統和Midjourney繪畫系統&#xff0c;支持OpenAI-GPT全模型國內AI全模型。本期針對源碼系統整體測試下來非常完美&#xff0c;那么如何搭建部署AI創作ChatGPT&#xff1f;小編這里寫一個詳細圖文教程吧。已支持GP…

CVE-2024-23334 AIOHTTP 目錄遍歷漏洞分析

漏洞描述&#xff1a; aiohttp 是一個用于 asyncio 和 Python 的異步 HTTP 客戶端/服務器框架。使用aiohttp作為Web服務器并配置靜態路由時&#xff0c;需要指定靜態文件的根路徑。此外&#xff0c;選項“follow_symlinks”可用于確定是否遵循靜態根目錄之外的符號鏈接。當“f…

css樣式元素的相對定位,絕對定位,固定定位等元素定位運用技巧詳解

文章目錄 1.相對定位 relative2.絕對定位 absolute3.固定定位4.display 轉換元素5.float浮動6.float產生內容塌陷問題7.overflow CSS樣式學習寶典&#xff0c;關注點贊加收藏&#xff0c;防止迷路哦 在CSS中關于定位的內容是&#xff1a;position:relative | absolute | static…

Unreal觸屏和鼠標控制旋轉沖突問題

Unreal觸屏和鼠標控制旋轉沖突問題 鼠標控制攝像機旋轉添加Input軸計算旋轉角度通過軸事件控制旋轉 問題和原因問題原因 解決辦法增加觸摸控制旋轉代碼觸屏操作下屏蔽鼠標軸響應事件 鼠標控制攝像機旋轉 通過Mouse X和Mouse Y控制攝像機旋轉。 添加Input軸 計算旋轉角度 通過…

SpringBootWeb快速入門

1.創建springboot工程&#xff0c;新建module 2.勾選web開發相關依賴 3.刪除多余文件 4.新建類 5.啟動類中運行main方法 6.啟動 默認端口號8080 7.打開瀏覽器&#xff0c;地址欄輸入 8.報錯 9.原因&#xff0c;控制層位置放錯&#xff0c;剪切controller層放進com.example …

[vue error] TypeError: Components is not a function

問題詳情 問題描述: element plus按需導入后&#xff0c;啟動項目報錯&#xff1a; 問題原因 unplugin-vue-components插件版本問題 查看 unplugin-vue-components插件可以發現版本太高了 問題解決 unplugin-vue-components 版本高了&#xff0c;我用的0.26.0&#xff0c…

AI寫的wordpress網站首頁模板 你覺得怎么樣?

以下是一個AI寫的基本的首頁模板示例&#xff0c;包含您提到的各個模塊。請注意&#xff0c;這只是一個基本框架&#xff0c;您可能需要根據您的具體需求進行進一步的定制和調整。 <!DOCTYPE html> <html <?php language_attributes(); ?>> <head>&…

【STM32+HAL】姿態傳感器陀螺儀MPU6050模塊

一、準備工作 有關OLED屏初始化的問題&#xff0c;詳見【STM32HAL】OLED顯示初始化配置 二、所用工具 1、芯片&#xff1a;STM32F10C8T6 2、CUBEMX配置軟件 3、 6 軸運動處理組件MPU6050 三、實現功能 OLED屏顯示姿態角 四、HAL配置步驟 1、開啟I2C1進行MPU6050通信 2、開…

供應鏈管理(SCM):界面設計全面掃盲,得供應鏈者得天下

大家伙&#xff0c;我是大千UI工場&#xff0c;專注UI分享和項目接單&#xff0c;本期帶來供應鏈系統的設計分享&#xff0c;歡迎大家關注、互動交流。 一、什么是SCM SCM系統是供應鏈管理&#xff08;Supply Chain Management&#xff09;系統的縮寫。供應鏈管理是指協調和管…

計算機視覺 了解OpenCV、COLMAP、PyTorch3D 和 OpenGL 中坐標系3D轉換的簡要指南

一、簡述 由于坐標系不同,在OpenCV、COLMAP、PyTorch3D和OpenGL等 3D 框架的世界中進行轉換可能會令人覺得頭疼。這里比較它們的坐標系并提供它們之間轉換的示例。核心還是找到在這些不同的 3D 環境中無縫工作所需的知識,讓我們以清晰直接的方式探索和理解這些坐標系。 2D 成…

【筆記版】edgecore.yaml分析總結

1. 文件路徑 /etc/kubeedge/config edgecore.yaml是該目錄下唯一的文件 附上鏈接&#xff1a;edgecore.yaml 2. 文件生成方式 2.1 方式一 使用keadm安裝部署的方式&#xff0c;執行完keadm join --cloudcore-ipportcloudcore監聽的IP地址:端口&#xff08;默認為10002&…

題目 1431: 藍橋杯第五屆真題-分糖果

題目描述: 有n個小朋友圍坐成一圈。老師給每個小朋友隨機發偶數個糖果&#xff0c;然后進行下面的游戲&#xff1a; 每個小朋友都把自己的糖果分一半給左手邊的孩子。 一輪分糖后&#xff0c;擁有奇數顆糖的孩子由老師補給1個糖果&#xff0c;從而變成偶數。 反復進行這個游戲…

設計模式精解:GoF 23種設計模式全解析

在軟件工程中&#xff0c;設計模式是為了解決常見的軟件設計問題而形成的一套經典解決方案。這些模式不僅能夠幫助開發者提高設計的靈活性和代碼的重用性&#xff0c;還能使問題的解決方案更加清晰、易于理解。《設計模式精解&#xff0d;GoF 23種設計模式》一書中所列舉的23種…

微信小程序的單位

在小程序開發中&#xff0c;rpx是一種相對長度單位&#xff0c;用于在不同設備上實現自適應布局。它是微信小程序特有的單位&#xff0c;表示屏幕寬度的 1/750。 rpx單位的好處在于可以根據設備的屏幕寬度進行自動換算&#xff0c;使得頁面在不同設備上保持一致的顯示效果。例…

學習筆記 前端

學習筆記 前端 學習記錄nodejsyarn解決方法 學習記錄 nodejs yarn 描述&#xff1a;想體驗一下chatgptnextweb在本地部署&#xff0c;但是本地部署需要yarn環境&#xff0c;網上看了yarn在node16以上就自帶了&#xff0c;而我的電腦是node18&#xff0c;所以就直接輸入了ya…

(十)SpringCloud系列——openfeign的高級特性實戰內容介紹

前言 本節內容主要介紹一下SpringCloud組件中微服務調用組件openfeign的一些高級特性的用法以及一些常用的開發配置&#xff0c;如openfeign的超時控制配置、openfeign的重試機制配置、openfeign集成高級的http客戶端、openfeign的請求與響應壓縮功能&#xff0c;以及如何開啟…

論文閱讀-高效構建檢查點

論文標題&#xff1a;On Efficient Constructions of Checkpoints 摘要 高效構建檢查點/快照是訓練和診斷深度學習模型的關鍵工具。在本文中&#xff0c;我們提出了一種適用于檢查點構建的有損壓縮方案&#xff08;稱為LC-Checkpoint&#xff09;。LC-Checkpoint同時最大化了…

MFC中CString的MakeUpper使用方法

在MFC中&#xff0c;CString類提供了MakeUpper函數來將字符串中的字符全部轉換為大寫。MakeUpper函數沒有參數&#xff0c;它會直接修改原始的CString對象。 下面是一些示例代碼&#xff0c;演示了如何使用MakeUpper函數&#xff1a; CString str "Hello, World!"…