如何确保前端Cookie内容没有被篡改?

柯福 Dev 阅读 63

最近在做用户登录功能时发现,如果直接用document.cookie = "userId=123",怎么防止中间人修改Cookie里的userId呢?

试过给Cookie加了加密,但后来想到就算加密了,攻击者可能还是能改密文导致解密失败。查资料说要验证完整性,但具体该怎么做呢?

看别人用过在Cookie里加签名字段,比如这样:


// 假设后端生成的签名逻辑
const secret = 'mysecret';
const payload = 'userId=123';
const hmac = crypto.createHmac('sha256', secret)
                  .update(payload)
                  .digest('hex');
document.cookie = `${payload};signature=${hmac}`;

但这样前端直接暴露了签名算法,攻击者能不能伪造签名?是不是应该完全在服务端处理?

我来解答 赞 7 收藏
二维码
手机扫码查看
2 条解答
Newb.雨萓
你这个问题其实很常见,特别是在做用户认证时,前端 Cookie 一旦暴露,攻击者确实可能篡改,甚至伪造。你提到签名这个思路是对的,但确实不能在前端处理签名,下面我来一步步讲怎么做比较安全。

第一步,前端不要自己构造 Cookie,也不要暴露签名逻辑。签名必须由后端生成,前端只负责接收和存储。攻击者如果能拿到你的签名算法和密钥,就完全可以伪造,不管你是加密还是哈希。

第二步,后端在用户登录成功之后,生成一个 payload,比如你提到的 userId=123,然后使用 HMAC-SHA256 对这个 payload 进行签名,签名结果附加在 Cookie 里,比如写成 userId=123;signature=xxxxx。后端代码大概是这样:

const crypto = require('crypto');

function generateCookiePayload(userId) {
const secret = 'your_very_strong_secret'; // 这个必须放在服务端,不能暴露
const payload = userId=${userId};
const hmac = crypto.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return ${payload};signature=${hmac};
}

// 假设用户登录成功
const userId = 123;
const cookieValue = generateCookiePayload(userId);
res.setHeader('Set-Cookie', auth_token=${cookieValue}; HttpOnly; Secure; SameSite=Strict);


这里 HttpOnly 是为了防止 XSS 读取 Cookie,Secure 是保证 Cookie 只通过 HTTPS 传输,SameSite=Strict 可以防止 CSRF 攻击,这些都是基础安全措施。

第三步,前端只需要接收这个 Cookie,不需要也不应该去设置它。前端在发送请求时,Cookie 会自动携带过去。后端每次收到请求,都要重新计算 payload 的签名,然后和 Cookie 中的 signature 比较。如果不一样,说明被篡改了,直接拒绝处理。

后端验证签名的逻辑大概如下:

function verifyCookieSignature(cookie) {
const secret = 'your_very_strong_secret';
const [payloadPart, signaturePart] = cookie.split(';signature=');
if (!payloadPart || !signaturePart) return false;

const expectedSignature = crypto.createHmac('sha256', secret)
.update(payloadPart)
.digest('hex');

return signaturePart === expectedSignature;
}

// 每次请求进来都做验证
const cookie = req.cookies.auth_token;
if (!verifyCookieSignature(cookie)) {
return res.status(401).send('Invalid or tampered token');
}


这样做的原理是:签名只能由拥有密钥的服务端生成和验证,攻击者就算能读到 Cookie 内容(比如通过中间人攻击),也无法伪造合法的签名,因为不知道密钥。而且即使他篡改了 payload,签名也会不匹配。

第四步,关于加密的问题,你担心的是对的。光加密还不够,因为攻击者可能截获密文,然后重放使用,或者篡改后导致解密失败。而使用签名可以保证数据的完整性,也就是 payload 没有被改动。

第五步,如果你需要更进一步的安全性,可以考虑使用 JWT。JWT 本质上也是类似的思路,但结构更规范,支持过期时间、加密签名等功能。但即使使用 JWT,也建议在服务端生成和验证,前端只负责存储和传递。

总结一下:

- 签名必须由服务端生成,密钥不能暴露给前端
- Cookie 要设置 HttpOnly、Secure、SameSite 等属性
- 每次请求都要验证签名是否合法
- 不要让前端处理 Cookie 内容或者签名逻辑

这样就能有效防止 Cookie 被篡改了。如果你还打算支持 Token 刷新、黑名单等功能,可以再引入 Redis 这样的中间存储,但这一步可以先不急。先保证 Cookie 本身的完整性,是最关键的。
点赞 4
2026-02-08 07:03
公孙雨橙
你说的这个签名方案确实是个方向,但问题的关键在于,签名验证这种事绝对不能让前端自己干。因为不管你怎么折腾,只要逻辑在前端实现,攻击者总能逆向出来,效率再高的加密也没用。

最简单的办法是把签名验证完全扔到服务端去处理。流程大概是这样的:

1. 后端生成Cookie的时候,同时计算一个HMAC签名(就像你代码里写的那样),然后把这个签名也塞进Cookie。
2. 每次请求到达后端时,后端重新根据收到的Cookie内容和自己的密钥计算一次签名,然后跟传上来的签名对比。
3. 如果签名对不上,直接丢弃这个Cookie,返回错误。

这样即使攻击者改了Cookie里的内容,他也没法伪造出正确的签名,因为不知道你的密钥。

另外提醒一下,别忘了给Cookie设置HttpOnlySecure属性,效率虽然会稍微低一点,但安全性提升很多,特别是防止XSS攻击。

至于具体代码,后端可以用类似下面的方式验证签名:

function verifyCookie(cookie, secret) {
const parts = cookie.split(';signature=');
if (parts.length !== 2) return false;

const payload = parts[0];
const receivedSignature = parts[1];
const expectedSignature = crypto.createHmac('sha256', secret)
.update(payload)
.digest('hex');

return crypto.timingSafeEqual(Buffer.from(receivedSignature), Buffer.from(expectedSignature));
}


记住,安全的事千万别指望前端来做,交给后端效率更高也更靠谱。
点赞 5
2026-01-30 06:10