1 -> 引入HTTP cookie
1.1 -> 定义
HTTP Cookie(也称为Web Cookie、浏览器Cookie或简称Cookie)是服务器发送到用户浏览器并保存在浏览器上的一小块数据,它会在浏览器之后向同一服务器再次发送请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态、记录用户偏好等。
1.2 -> 工作原理
- 当用户第一次访问网站时,服务器会在响应的HTTP头中设置Set-Cookie字段,用于发送Cookie到用户的浏览器。
- 浏览器在接收到Cookie后,会将其保存在本地(通常是按照域名进行存储)。
- 在之后的请求中,浏览器会自动在HTTP请求头中携带Cookie字段,将之前保存的Cookie信息发送给服务器。
1.3 -> 分类
- 会话**Cookie(Session Cookie)**:在浏览器关闭时失效。
- 持久**Cookie(Persistent Cookie)**:带有明确的过期日期或持续时间,可以跨多个浏览器会话存在。
- 如果cookie是一个持久性的cookie,那么它其实就是浏览器相关的,特定目录下的一个文件。但直接查看这些文件可能会看到乱码或无法读取的内容,因为cookie文件通常以二进制或sqlite格式存储。一般我们查看,直接在浏览器对应的选项中直接查看即可。
类似下面这种方式:
1.4 -> 安全性
由于Cookie是存储在客户端的,因此存在被篡改或窃取的风险。
用途
- 用户认证和会话管理(最重要)
- 跟踪用户行为
- 缓存用户偏好等
- 比如在chrome浏览器下,可以直接访问:chrome://settings/cookies
2 -> 认识cookie
- HTTP存在一个报头选项:Set-Cookie,可以用来进行给浏览器设置Cookie值。
- 在HTTP响应头中添加,客户端(如浏览器)获取并自行设置并保存Cookie。
2.1 -> 基本格式
Set - Cookie: < name >= <value>
其中:
<name>是Cookie的名称,<value>是Cookie的值。
完整的Set - Cookie
Set - Cookie: username = zzl; expires = Thu, 5 Dec 2024 12:00:00
UTC; path = / ; domain = .example.com; secure; HttpOnly
时间格式必须遵守RFC 1123标准,具体格式样例:Tue, 01 Jan 2030 12:34:56 GMT或者UTC(推荐)。
关于时间解释
- Tue:星期二(星期几的缩写)
- ,:逗号分隔符
- 01:日期(两位数表示)
- Jan:一月(月份的缩写)
- 2030:年份(四位数)
- 12:34:56:时间(小时、分钟、秒)
- GMT:格林威治标准时间(时区缩写)
2.2 -> GMT vs UTC
GMT(格林威治标准时间)和 UTC(协调世界时)是两个不同的时间标准,但它们在大多数情况下非常接近,常常被混淆。以下是两者的简单解释和区别:
1. GMT(格林威治标准时间):
- GMT是格林威治标准时间的缩写,它是以英国伦敦的格林威治区为基准的世界时间标准。
- GMT不受夏令时或其他因素的影响,通常用于航海、航空、科学、天文等领域。
- GMT的计算方式是基于地球的自转和公转。
2. UTC(协调世界时):
- UTC全称为“协调世界时”,是国际电信联盟(ITU)制定和维护的标准时间。
- UTC的计算方式是基于原子钟,而不是地球的自转,因此它比GMT更准确。据称,世界上最精确的原子钟50亿年才会误差1秒。
- UTC是现在用的时间标准,多数全球性的网络和软件系统将其作为标准时间。
GMT和UTC的英文全称以及相关信息如下:
1. GMT(格林威治标准时间)
- 英文全称:Greenwich Mean Time
- GMT是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义为通过那里的经线。理论上来说,格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时的时间。
- 但值得注意的是,地球的自转是有些不规则的,且正在缓慢减速。因此,格林尼治时间已经不再被作为标准时间使用。
2. UTC(协调世界时)
英文全称:Coordinated Universal Time
- UTC是最主要的世界时间标准,其以原子时秒长为基础,在时刻上尽量接近于格林尼治标准时间。
- UTC被广泛使用在计算机网络、航空航天等领域,因为它提供了非常准确和可靠的时间参考。
总结来说,GMT和UTC都曾是或现在是国际上重要的时间标准,但由于地球自转的不规则性和原子钟的精确性,UTC已经成为了全球性的标准时间,而GMT则更多被用作历史和地理上的参考。
区别:
- 计算方式:GMT基于地球的自转和公转,而UTC基于原子钟。
- 准确度:由于UTC基于原子钟,它比基于地球自转的GMT更加精确。
在实际使用中,GMT和UTC之间的差别通常很小,大多数情况下可以交换使用。但在需要高精度时间计量的场合,如科学研究、网络通信等,UTC是更为准确的选择。
关于其他可选属性的解释
- **expires=<data>[要验证]**:设置Cookie的过期时期/时间。如果未指定此属性,则Cookie默认为会话Cookie,即当浏览器关闭时过期。
- **path=<some_path>[要验证]**:限制Cookie发送到服务器的哪些路径。默认为设置它的路径。
- **domain=<domain_name>**:指定哪些主机可以接受该Cookie。默认为设置它的主机。
- secure:仅当使用HTTPS协议时才发送Cookie。这有助于防止Cookie在不安全的HTTP连接中被截获。
- HttpOnly:标记Cookie为HttpOnly,意味着该Cookie不能被客户端脚本(如JavaScript)访问。这有助于防止跨站脚本攻击(XSS)。
以下是对Set-Cookie头部字段的简洁介绍
属性值描述usernamezzl这是Cookie的名称和值,标识用户名为“zzl”。expiresThu,5 Dec 2024 12:00:00 UTC指定Cookie的过期时间。在这个例子中,Cookie将在2024年12月5日12:00:00 UTC后过期。**path/定义Cookie的作用范围。这里设置的根路径为/,意味着Cookie对.example.com域名下的所有路径都可以用。domain****.example.com指定哪些域名可以接收这个Cookie。点前缀(.)表示包括所有的子域名。secure****-指示Cookie只能通过HTTPS协议发送,不能通过HTTP协议发送,增加安全性。HttpOnly****-**阻止客户端脚本(如JavaScript)访问此Cookie,有助于防止跨站脚本攻击(XSS)。
注意:
- 每个Cookie属性都以分号(;)和空格( )分割。
- 名称和值之间使用等号(=)分隔。
- 如果Cookie的名称或值包含特殊字符(如空格、分号、逗号等),则需要进行URL编码。
3 -> cookie的生命周期
- 如果设置了expires属性,则Cookie将在指定的日期/时间后过期。
- 如果没有设置expires属性,则Cookie默认为会话Cookie,即当浏览器关闭时过期。
3.1 -> 安全性考虑
安全性考虑
- 使用secure标志可以确保Cookie仅在HTTPS连接上发送,从而提高安全性。
- 使用HttpOnly标志可以防止客户端脚本(如JavaScript)访问Cookie,从而防止XSS攻击。
- 通过合理设置Set-Cookie的格式和属性,可以确保Cookie的安全性、有效性和可访问性,从而满足Web应用程序的需求。
3.2 -> 安全测试cookie
Comm.hpp HttpProtocol.hpp InetAddr.hpp LockGuard.hpp Log.hpp
Main.cc Makefile Socket.hpp TcpServer.hpp Thread.hpp
ThreadPool.hpp
3.2.1 -> 测试cookie写入浏览器
3.2.2 -> 测试自动提交
- 刷新浏览器,刚刚写入的cookie会自动被提交给服务器端。
3.2.3 -> 测试写入过期时间
- 这里要自己形成UTC统一标准时间,以下是C++代码。
std::string GetMonthName(int month)
{
std::vector<std::string> months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
return months[month];
}
std::string GetWeekDayName(int day)
{
std::vector<std::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];
//时间格式如: expires=Thu, 18 Dec 2024 12:00:00 UTC
snprintf(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_year + 1900,
tm->tm_hour,
tm->tm_min,
tm->tm_sec
);
return timebuffer;
}
3.2.4 -> 测试路径path
提交到非/a/b路径下
提交到/a/b路径下
核心代码:
class Http
{
private:
std::string GetMonthName(int month)
{
std::vector<std::string> months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
return months[month];
}
std::string GetWeekDayName(int day)
{
std::vector<std::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];
//时间格式如: expires=Thu, 18 Dec 2024 12:00:00 UTC
snprintf(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_year + 1900,
tm->tm_hour,
tm->tm_min,
tm->tm_sec
);
return timebuffer;
}
public:
Http(uint16_t port)
{
_tsvr = std::make_unique<TcpServer>(port,
std::bind(&Http::HandlerHttp, this, std::placeholders::_1));
_tsvr->Init();
}
std::string ProveCookieWrite() // 证明 cookie 能被写入浏览器
{
return "Set-Cookie: username=zhangsan;";
}
std::string ProveCookieTimeOut()
{
return "Set-Cookie: username=zhangsan; expires=" +
ExpireTimeUseRfc1123(60) + ";"; // 让 cookie 1min 后过期
}
std::string ProvePath()
{
return "Set-Cookie: username=zhangsan; 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.AddContent("<html><h1>helloworld</h1></html>");
return resp.Serialize();
}
void Run()
{
_tsvr->Start();
}
~Http()
{}
private:
std::unique_ptr<TcpServer> _tsvr;
};
4 -> 单独使用Cookie会存在什么问题
- 安全性问题:Cookie中的数据是以明文形式传输的,这意味着在网络传输过程中,数据可能会被截取或篡改。例如,在购物场景中,如果Cookie包含了应付款项等敏感信息,攻击者可以通过在Cookie传回到服务器之前将付款值改小的操作来达到非法目的。此外,存储于本地的密码等敏感信息也可能被钓鱼网站的服务器暴力获取。
- 隐私问题:一些网站可能会利用Cookie来跟踪用户的浏览行为,这可能会涉及到用户的隐私问题。例如,某些网站可能会通过Cookie来记录用户的浏览历史,以便进行针对性的广告推送等操作。
- 数据完整性问题:由于Cookie存储在客户端,用户可以通过浏览器的设置来修改或删除Cookie,这可能会导致数据的完整性受到影响。例如,如果用户修改了Cookie中的某些关键信息,可能会导致服务器无法正确识别用户的身份或状态。
- 性能问题:Cookie会被附加在每个HTTP请求中,这无形中增加了网络流量,尤其是当Cookie数据量较大时,可能会对网络性能产生一定的影响。
- 存储限制问题:Cookie的大小通常有限制(一般在4KB左右),对于一些复杂的存储需求来说可能不够用。例如,如果需要在客户端存储大量的用户数据或者复杂的应用状态信息,Cookie可能无法满足需求。
为了解决这些问题,通常会结合使用Cookie和Session等其他技术。Session将信息默认存储在服务端数据库的表中,而只给浏览器返回一把解锁Session的钥匙(即Session ID),这样可以在一定程度上提高数据的安全性和完整性,同时也能更好地管理用户的会话状态。
感谢各位大佬支持!!!
互三啦!!!
版权归原作者 枫叶丹4 所有, 如有侵权,请联系我们删除。