You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

document.cookie能否安全替代res.cookie?res.cookie失效问题排查

问题描述

我正在开发一个React+Node.js+MySQL项目,已实现用户登录功能。为实现用户身份识别,我采用JWT并将其存储在cookie中。

尝试1:后端通过res.cookie设置cookie

后端登录接口代码:

const login = (req, res) => {
  //CHECK USER

  const q = "SELECT * FROM users WHERE username = ?";

  db.query(q, [req.body.username], (err, data) => {
    if (err) return res.status(500).json(err);
    if (data.length === 0) return res.status(404).json("User not found!");

    //Check password
    const isPasswordCorrect = bcrypt.compareSync(
      req.body.password,
      data[0].password
    );

    if (!isPasswordCorrect)
      return res.status(400).json("Wrong username or password!");

    const token = jwt.sign({ id: data[0].id }, "jwtkey");
    const { password, ...other } = data[0];

    res.cookie('token', token, {httpOnly: true}).status(200).json(other)
  });
};

此时响应头中能看到Set-Cookie,但浏览器应用面板中看不到该cookie。

尝试2:前端通过document.cookie设置cookie

后端代码

const login = (req, res) => {
  //CHECK USER

  const q = "SELECT * FROM users WHERE username = ?";

  db.query(q, [req.body.username], (err, data) => {
    if (err) return res.status(500).json(err);
    if (data.length === 0) return res.status(404).json("User not found!");

    //Check password
    const isPasswordCorrect = bcrypt.compareSync(
      req.body.password,
      data[0].password
    );

    if (!isPasswordCorrect)
      return res.status(400).json("Wrong username or password!");

    const token = jwt.sign({ id: data[0].id }, "jwtkey");
    const { password, ...other } = data[0];

    return res.json({
      status: 200,
      token: token,
      other: other,
    });
  });
};

前端代码

const handleSubmit = async (e) => {
  e.preventDefault();
  try {
    const res = await axios.post(
      "http://localhost:8080/api/auth/login",
      inputs,
      {
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
        },
      }
    );
    const data = await res.data;
    if (data.status === 200) {
      document.cookie = `token=${data.token}; path=/`; // Set the token as a cookie
      navigate("/");
    }
  } catch (error) {
    setError(error.response.data);
  }
};

此时能在应用面板中看到cookie。

我的问题:

  1. 这种前端设置cookie的方式是否安全?
  2. 若不安全,还有什么替代方案?
  3. 我的res.cookie设置是否存在错误?

回答

1. 前端通过document.cookie设置token的安全性

不安全,核心风险如下:

  • 无HttpOnly属性:前端JS可直接读取该cookie,一旦页面存在XSS漏洞,攻击者能通过脚本窃取token,冒充用户身份。
  • 缺少Secure属性:若网站未强制HTTPS,cookie会以明文形式在网络传输,易被中间人劫持。
  • 未设置SameSite属性:默认策略下存在CSRF攻击风险,攻击者可诱导用户在已登录状态下触发带cookie的恶意请求。

2. 安全的替代方案

核心方案是回到后端通过res.cookie设置HttpOnly cookie,这是存储JWT最安全的方式之一,需调整以下配置:

(1)修复跨域cookie存储问题

你之前后端设置cookie后浏览器看不到,大概率是跨域配置缺失:

  • 后端CORS配置:开启credentials支持,以cors中间件为例:
    const cors = require('cors');
    app.use(cors({
      origin: "http://localhost:3000", // 你的前端域名
      credentials: true // 允许跨域携带cookie
    }));
    
  • 前端请求配置:axios请求需添加withCredentials: true,让浏览器自动处理跨域cookie:
    const res = await axios.post(
      "http://localhost:8080/api/auth/login",
      inputs,
      {
        withCredentials: true, // 关键配置
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
        },
      }
    );
    

(2)完善res.cookie的安全属性

后端设置cookie时,添加以下安全属性:

res.cookie('token', token, {
  httpOnly: true, // 禁止前端JS读取,防XSS
  secure: process.env.NODE_ENV === 'production', // 生产环境仅HTTPS传输
  sameSite: 'strict', // 严格限制跨站请求携带cookie,防CSRF
  path: '/', // 全站有效
  maxAge: 24 * 60 * 60 * 1000 // 有效期1天,可按需调整
}).status(200).json(other);

3. 你之前res.cookie设置的错误

并非语法错误,而是跨域场景下缺少必要配置:

  • 后端未开启CORS的credentials支持,导致浏览器因跨域安全策略拒绝存储Set-Cookie。
  • 前端请求未携带withCredentials: true,浏览器不会处理跨域的cookie设置。
  • 若生产环境使用HTTPS,未设置secure: true也会导致浏览器拒绝存储cookie。

内容的提问来源于stack exchange,提问作者Vayun Ekbote

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 14:07:44