如何确保Cookie内容不被篡改?Secure+HttpOnly还不够吗?
在开发登录功能时,我设置了Cookie的Secure和HttpOnly属性,但测试发现攻击者仍能修改Cookie中的role字段(比如把普通用户改成管理员)。除了用HTTPS加密传输,还有哪些方法能验证Cookie内容的完整性?服务器怎么知道接收到的Cookie是否被篡改过?
比如前端设置Cookie的代码是这样的:
document.cookie = "user=admin; HttpOnly; Secure";
但服务器返回的响应头也有类似设置,可攻击者抓包后直接改值,服务器居然接受了…
试过在Cookie值里加时间戳,但攻击者也能同步修改时间戳,感觉没用。难道要给Cookie内容加签名?具体该怎么实现?
通常的做法是:在 Cookie 中保存数据的同时,附带一个服务端生成的签名,每次请求过来都重新校验这个签名有没有被改过。
比如你现在的写法是:
document.cookie = "user=admin; HttpOnly; Secure";
这样当然危险,攻击者可以篡改成任意值。你得改成类似:
user=admin&role=user&sig=xxxxxx
其中 sig 是 user 和 role 的签名,服务端每次收到 Cookie 后,先提取 user 和 role,再重新计算一次签名,跟传过来的 sig 比较,不一致就说明被篡改了。
签名算法简单点用 HMAC 就够了,比如在 Node.js 中代码给你:
const crypto = require('crypto');
const secret = 'your-secret-key'; // 一定要保密
const data = 'user=admin&role=user';
const sig = crypto.createHmac('sha256', secret).update(data).digest('hex');
然后写 Cookie 的时候是:
res.setHeader('Set-Cookie',
user=admin; role=user; sig=${sig}; Secure; HttpOnly);接收请求时再把 user 和 role 拿出来重新算一遍 sig,对比是否一致。
当然了,这种方案 secret 一定要保密,不能泄露,否则签名就失效了。
如果你还担心重放攻击,可以在签名内容里加一个时间戳,服务端校验时间是否在容忍范围内(比如5分钟内有效)。
总结一下:
1. Secure + HttpOnly 是基础,不能少
2. 要防止篡改,必须加签名
3. 签名算法建议用 HMAC-SHA256
4. 签名密钥要保密,不能暴露给前端或日志
5. 有更高安全要求的,可以考虑 JWT 或者加密 session token 存服务端
这样攻击者就算能改 Cookie,也改不了签名,服务端一校验就能发现。
简单实现如下:
记得服务器保存secret密钥,别泄露给前端。