微信营销手机,网站建设备案优化设,内网穿透做网站,建设网站需要些什么手续#x1f351;个人主页#xff1a;Jupiter. #x1f680; 所属专栏#xff1a;Linux从入门到进阶 欢迎大家点赞收藏评论#x1f60a; 目录 HTTP Cookie定义工作原理分类安全性用途 认识 cookie基本格式实验测试 cookie 当我们登录了B站过后#xff0c;为什么下次访问B站就… 个人主页Jupiter. 所属专栏Linux从入门到进阶 欢迎大家点赞收藏评论 目录 HTTP Cookie定义工作原理分类安全性用途 认识 cookie基本格式实验测试 cookie 当我们登录了B站过后为什么下次访问B站就不需要登陆了
问题B 站是如何认识我这个登录用户的问题HTTP 是无状态无连接的怎么能够记住我 HTTP Cookie
定义
HTTP Cookie也称为 Web Cookie、浏览器 Cookie 或简称 Cookie是服务器发送到用户浏览器并保存在浏览器上的一小块数据它会在浏览器之后向同一服务器再次发起请求时被携带并发送到服务器上。通常它用于告知服务端两个请求是否来自同一浏览器如保持用户的登录状态、记录用户偏好等。
工作原理
当用户第一次访问网站时服务器会在响应的 HTTP 头中设置 Set-Cookie字段如Set-Cookie : user zhangsan用于发送 Cookie 到用户的浏览器。浏览器在接收到 Cookie 后会将其保存在本地通常是按照域名进行存储。在之后的请求中浏览器会自动在 HTTP 请求头中携带 Cookie 字段将之前保存的 Cookie 信息发送给服务器。
分类 会话 CookieSession Cookie在浏览器关闭时失效。 持久 CookiePersistent Cookie带有明确的过期日期或持续时间可以跨多个浏览器会话存在。 如果 cookie 是一个持久性的 cookie那么它其实就是浏览器相关的特定目录下的一个文件。但直接查看这些文件可能会看到乱码或无法读取的内容因为 cookie 文件通常以二进制或 sqlite 格式存储。一般我们查看直接在浏览器对应的选项中直接查看即可。 类似于下面这种方式 安全性
由于 Cookie 是存储在客户端的因此存在被篡改或窃取的风险。
用途
用户认证和会话管理(最重要)跟踪用户行为缓存用户偏好等比如在 chrome 浏览器下可以直接访问:link 认识 cookie
HTTP 存在一个报头选项Set-Cookie, 可以用来进行给浏览器设置 Cookie值。在 HTTP 响应报头中添加客户端如浏览器获取并自行设置并保存Cookie。
服务器发送Cookie
当客户端如浏览器首次请求服务器资源时服务器可能会在HTTP响应中包含一个或多个Set-Cookie头部。这些Set-Cookie头部指示客户端存储特定的信息即Cookie。每个Set-Cookie头部都包含了Cookie的名称、值以及可选的属性如过期时间Expires/Max-Age、作用域Path、安全性要求Secure、跨站策略SameSite以及是否只能通过HTTP接口访问HttpOnly等。
客户端接收并保存Cookie
浏览器接收到包含Set-Cookie头部的HTTP响应后会解析这些头部并根据其中的指令将Cookie存储到本地。存储的Cookie会包含名称、值以及所有相关的属性。浏览器会根据Cookie的过期时间和其他属性来决定何时删除这些Cookie。
客户端发送Cookie
当浏览器再次向同一服务器或符合Cookie作用域的其他服务器发送请求时它会自动检查是否有与该请求相关的Cookie。如果有浏览器会将这些Cookie附加到HTTP请求的Cookie头部并发送给服务器。服务器接收到请求后可以从Cookie头部中读取这些Cookie并根据需要处理它们。
基本格式 完整的 Set-Cookie 示例 时间格式必须遵守 RFC 1123 标准具体格式样例Tue, 01 Jan 2030 12:34:56 GMT 或者 UTC(推荐)。 关于时间解释
Tue: 星期二星期几的缩写, : 逗号分隔符18: 日期两位数表示Thu: 月份的缩写2024: 年份四位数12:34:56: 时间小时、分钟、秒GMT: 格林威治标准时间时区缩写
GMT 和 UTC 都曾是或现在是国际上重要的时间标准但由于地球自转的不规则性和原子钟的精确性UTC 已经成为了全球性的标准时间而 GMT 则更多被用作历史和地理上的参考。
关于其他可选属性的解释
expiresdate设置 Cookie 的过期日期/时间。如果未指定此属性则 Cookie 默认为会话 Cookie即当浏览器关闭时过期。pathsome_path限制 Cookie 发送到服务器的哪些路径。默认为设置它的路径。domaindomain_name指定哪些主机可以接受该 Cookie。默认为设置它的主机。secure仅当使用 HTTPS 协议时才发送 Cookie。这有助于防止Cookie 在不安全的 HTTP 连接中被截获。HttpOnly标记 Cookie 为 HttpOnly意味着该 Cookie 不能被客户端脚本如 JavaScript访问。这有助于防止跨站脚本攻击XSS。
以下是对 Set-Cookie 头部字段的简洁介绍 注意事项
每个 Cookie 属性都以分号;和空格 分隔。名称和值之间使用等号分隔。如果 Cookie 的名称或值包含特殊字符如空格、分号、逗号等则需要进行 URL 编码。
Cookie 的生命周期
如果设置了 expires 属性则 Cookie 将在指定的日期/时间后过期。如果没有设置 expires 属性则 Cookie 默认为会话 Cookie即当浏览器关闭时过期。
安全性考虑
使用 secure 标志可以确保 Cookie 仅在 HTTPS 连接上发送从而提高安全性。使用 HttpOnly 标志可以防止客户端脚本如 JavaScript访问 Cookie从而防止 XSS 攻击。通过合理设置 Set-Cookie 的格式和属性可以确保 Cookie 的安全性、有效性和可访问性从而满足 Web 应用程序的需求。
实验测试 cookie
测试 cookie 的关键性完整代码全部附在最后。
测试 cookie 写入到浏览器 resp.AddHeader(Set-Cookie: usernamezhangsan;); //响应中添加一行报头即可测试自动提交 测试写入过期时间 这里要由我们自己形成 UTC 统一标准时间 //时间格式如: expiresThu, 18 Dec 2024 12:00:00 UTCstd::string GetMonthName(int month) {std::vectorstd::string months {Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec};return months[month];}std::string GetWeekDayName(int day){std::vectorstd::string weekdays {Sun, Mon, Tue, Wed, Thu, Fri, Sat};return weekdays[day];}std::string ExpireTimeUseRfc1123(int t) // 秒级别的未来UTC时间{time_t timeout time(nullptr) t;struct tm *tm gmtime(timeout); // 这里不能用localtime因为localtime是默认带了时区的. gmtime获取的就是UTC统一时间char timebuffer[1024];//时间格式如: expiresThu, 18 Dec 2024 12:00:00 UTCsnprintf(timebuffer, sizeof(timebuffer), %s, %02d %s %d %02d:%02d:%02d UTC, GetWeekDayName(tm-tm_wday).c_str(),tm-tm_mday,GetMonthName(tm-tm_mon).c_str(),tm-tm_year1900,tm-tm_hour,tm-tm_min,tm-tm_sec);return timebuffer;} 测试路径 path 提交到非/a/b 路径下
比如http://8.137.19.140:8888/a/x比如http://8.137.19.140:8888/比如http://8.137.19.140:8888/x/y 单独使用 Cookie有什么问题
我们写入的是测试数据如果写入的是用户的私密数据呢比如用户名密码浏览痕迹等。本质问题在于这些用户私密数据在浏览器(用户端)保存非常容易被人盗取更重要的是除了被盗取还有就是用户私密数据也就泄漏了。 Cookie测试代码
#pragma once#include iostream
#include string
#include sstream
#include vector
#include memory
#include ctime
#include TcpServer.hppconst std::string HttpSep \r\n;// 可以配置的
const std::string homepage index.html;
const std::string wwwroot ./wwwroot;class HttpRequest
{
public:HttpRequest() : _req_blank(HttpSep), _path(wwwroot){ }bool GetLine(std::string str, std::string *line){auto pos str.find(HttpSep);if (pos std::string::npos)return false;*line str.substr(0, pos); // \r\nstr.erase(0, pos HttpSep.size());return true;}bool Deserialize(std::string request){std::string line;bool ok GetLine(request, line);if (!ok)return false;_req_line line;while (true){bool ok GetLine(request, line);if (ok line.empty()){_req_content request;break;}else if (ok !line.empty()){_req_header.push_back(line);}else{break;}}return true;}~HttpRequest(){}
private:// http报文自动std::string _req_line; // method url http_versionstd::vectorstd::string _req_header;std::string _req_blank;std::string _req_content;// 解析之后的内容std::string _method;std::string _url; // /dira/dirb/x.html /dira/dirb/XX?usrname100password1234 /dira/dirbstd::string _http_version;std::string _path; // ./wwwrootstd::string _suffix; // 请求资源的后缀
};const std::string BlankSep ;
const std::string LineSep \r\n;class HttpResponse
{
public:HttpResponse() : _http_version(HTTP/1.0), _status_code(200), _status_code_desc(OK), _resp_blank(LineSep){}void SetCode(int code){_status_code code;}void SetDesc(const std::string desc){_status_code_desc desc;}void MakeStatusLine(){_status_line _http_version BlankSep std::to_string(_status_code) BlankSep _status_code_desc LineSep;}void AddHeader(const std::string header){_resp_header.push_back(headerLineSep);}void AddContent(const std::string content){_resp_content content;}std::string Serialize(){MakeStatusLine();std::string response_str _status_line;for (auto header : _resp_header){response_str header;}response_str _resp_blank;response_str _resp_content;return response_str;}~HttpResponse() {}
private:std::string _status_line;std::vectorstd::string _resp_header;std::string _resp_blank;std::string _resp_content; // body// httpversion StatusCode StatusCodeDescstd::string _http_version;int _status_code;std::string _status_code_desc;
};class Http
{
private:std::string GetMonthName(int month) {std::vectorstd::string months {Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec};return months[month];}std::string GetWeekDayName(int day){std::vectorstd::string weekdays {Sun, Mon, Tue, Wed, Thu, Fri, Sat};return weekdays[day];}std::string ExpireTimeUseRfc1123(int t) // 秒级别的未来UTC时间{time_t timeout time(nullptr) t;struct tm *tm gmtime(timeout); // 这里不能用localtime因为localtime是默认带了时区的. gmtime获取的就是UTC统一时间char timebuffer[1024];//时间格式如: expiresThu, 18 Dec 2024 12:00:00 UTCsnprintf(timebuffer, sizeof(timebuffer), %s, %02d %s %d %02d:%02d:%02d UTC, GetWeekDayName(tm-tm_wday).c_str(),tm-tm_mday,GetMonthName(tm-tm_mon).c_str(),tm-tm_year1900,tm-tm_hour,tm-tm_min,tm-tm_sec);return timebuffer;}
public:Http(uint16_t port){_tsvr std::make_uniqueTcpServer(port, std::bind(Http::HandlerHttp, this, std::placeholders::_1));_tsvr-Init();}std::string ProveCookieWrite() // 证明cookie能被写入浏览器{return Set-Cookie: usernamezhangsan;;}// resp.AddHeader(ProveCookieWrite()); //测试cookie被写入与自动提交std::string ProveCookieTimeOut(){return Set-Cookie: usernamezhangsan; expires ExpireTimeUseRfc1123(60) ;; // 让cookie 1min后过期}std::string ProvePath(){return Set-Cookie: usernamezhangsan; path/a/b;;}std::string ProveOtherCookie(){return Set-Cookie: passwd1234567890; path/a/b;;}std::string HandlerHttp(std::string request){HttpRequest req;req.Deserialize(request);req.DebugHttp();lg.LogMessage(Debug, %s\n, ExpireTimeUseRfc1123(60).c_str());HttpResponse resp;resp.SetCode(200);resp.SetDesc(OK);resp.AddHeader(Content-Type: text/html);// resp.AddHeader(ProveCookieWrite()); //测试cookie被写入与自动提交// resp.AddHeader(ProveCookieTimeOut()); //测试过期时间的写入// resp.AddHeader(ProvePath()); // 测试路径resp.AddHeader(ProvePath());resp.AddHeader(ProveOtherCookie());resp.AddContent(htmlh1helloworld/h1/html);return resp.Serialize();}void Run(){_tsvr-Start();}~Http(){}
private:std::unique_ptrTcpServer _tsvr;
};