Nodejs 關於 Cookie的一些問題

Nodejs 關於 Cookie的一些問題

最近正在學習 ndoe.js 的 cookie 相關操作,發現一個問題:

app.configure(function() {
    app.use(express.cookieParser('jfvstest'));
    app.use(express.cookieSession( {
        key: 'test',
        secret: 'HelloExpressSESSION'
    }));
app.get('/google', function(req, res) {
    req.session.google = "google";
    req.session.password = "123456";
    console.log(req.cookies);
    res.send("ok");
    res.end();
});

我用瀏覽器套件觀察 cookie value 發現基本上依然是明文:

s%3Aj%3A%7B%22google%22%3A%22google%22%2C%22password%22%3A%22123456%22%7D.T0VjxprKtgC80%2Fuhfcs62mSsoe7sNC2Eo9KpV%2Bt0xr0

書上寫說 secret 的用途是:

Express 需要使用這組 secret 字串去加密 cookie,以防 cookie被破解。

有用 secret 的話,貌似依然是明文? (實際驗證的方式是我寫了另外一個 test.js 用,沒有相關 secret,使用: console.log(req.cookies) 可以直接得到明文: { test: 's:j:{"google":"google","password":"123456"}.rsAPPiLlP6Of0KWqCxdRIxfR2NHKL3ibs+ZSIZq3gn4' }

secret 改變的話,實際影響的只有 rsAPPiLlP6Of0KWqCxdRIxfR2NHKL3ibs+ZSIZq3gn4 這串,可是 會話cookie 會完全明文存在客戶端。

我又做了一個小實驗,我在瀏覽器中修改 s%3Aj%3A%7B%22google%22%3A%22google%22%2C%22password%22%3A%22123456%22%7D.T0VjxprKtgC80%2Fuhfcs62mSsoe7sNC2Eo9KpV%2Bt0xr0 將 google 改成 gosogle,在 console.log(req.cookies) 三次: { ming: ‘s:j:{“gosogle”:“google”,“password”:“123456”}.OiVYufTj5DIdmvBy7/f03poRosZ/Vwwf1rYzGCQ1Z7o’ } { ming: ‘s:j:{}.jNSi4gEWlIHY0qiHpvD4By+LOak9MErZOoOCAqh/oQM’ } { ming: ‘s:j:{}.jNSi4gEWlIHY0qiHpvD4By+LOak9MErZOoOCAqh/oQM’ } 第一次得到的是我修改過的內容,第二次直接變成空值,再用瀏覽器看 cookie 值: s%3Aj%3A%7B%7D.jNSi4gEWlIHY0qiHpvD4By%2BLOak9MErZOoOCAqh%2FoQM 有別於: s%3Aj%3A%7B%22google%22%3A%22google%22%2C%22password%22%3A%22123456%22%7D.T0VjxprKtgC80%2Fuhfcs62mSsoe7sNC2Eo9KpV%2Bt0xr0

問題:

  1. 是哪些原因造成這些總總原因?
  2. 我想要讓 cookie 內容為加密狀態,除了自己加密 cookie 內容在丟給客戶瀏覽器之外,有沒有更優雅的方式?
  3. 最後一段修改實驗,第一次讀入的修改數據會是正常的,第二次就會被修改成空 {} 是何原因? 感謝!

4 回复

Node.js 中关于 Cookie 的一些问题

问题背景

最近在学习 Node.js 的 cookie 相关操作时,遇到了一些困惑。特别是关于如何使用 express.cookieParserexpress.cookieSession 来加密和保护 session 数据。

示例代码

const express = require('express');
const app = express();

// 使用 cookieParser 和 cookieSession
app.use(express.cookieParser('jfvstest'));
app.use(express.cookieSession({
    key: 'test',
    secret: 'HelloExpressSESSION'
}));

app.get('/google', function(req, res) {
    req.session.google = "google";
    req.session.password = "123456";
    console.log(req.cookies);
    res.send("ok");
    res.end();
});

app.listen(3000, () => {
    console.log('Server is running on port 3000');
});

问题分析

  1. 为什么 cookie 仍然是明文?

    • express.cookieParserexpress.cookieSession 并不直接对 cookie 进行加密,它们主要用于解析和签名 cookie。
    • secret 主要用于签名 cookie,防止篡改。但 cookie 的内容本身并没有被加密,只是经过 Base64 编码。
  2. 如何让 cookie 内容为加密状态?

    • 你可以使用第三方库如 cookie-encrypt 或者 cookie-cipher 来加密 cookie 的内容。
    • 示例代码如下:
const express = require('express');
const cookieEncrypt = require('cookie-encrypt');

const app = express();

// 创建一个密钥
const secretKey = 'your-secret-key';

// 使用 cookieParser 解析 cookie
app.use(express.cookieParser(secretKey));

// 自定义中间件来加密 cookie
app.use((req, res, next) => {
    // 加密 session 数据
    req.session = cookieEncrypt.encrypt(req.session, secretKey);
    next();
});

app.get('/google', function(req, res) {
    req.session.google = "google";
    req.session.password = "123456";
    console.log(req.cookies);
    res.send("ok");
    res.end();
});

app.listen(3000, () => {
    console.log('Server is running on port 3000');
});
  1. 为什么第一次读取到修改后的数据,第二次变成空值?
    • 这可能是因为 session 数据没有正确地更新或存储。当你修改 cookie 的内容后,如果没有正确地保存 session,那么下次请求时 session 数据将为空。
    • 确保每次修改 session 数据后都调用 req.session.save() 方法来保存更改。

总结

  • express.cookieParserexpress.cookieSession 只是用于解析和签名 cookie,而不是加密。
  • 要实现加密,可以使用第三方库如 cookie-encrypt
  • 确保每次修改 session 数据后调用 req.session.save() 方法来保存更改。

希望这些信息能帮助你更好地理解和解决这些问题。


这个secret是用于防篡改标记session的那个cookie值的

cookie里放的内容就应该不怕客户端看,不想泄漏的话那就放到session里吧。

回答

1. 造成这些现象的原因

首先,express.cookieSessionexpress.cookieParser 在较旧版本的 Express 中用于处理 session 和 cookies。然而,在现代 Node.js 应用程序中,建议使用 express-sessioncookie-parser 这两个中间件。

  • 明文存储: req.session 存储的是 session 数据,而不是直接存储到客户端的 cookie 中。session 数据是存储在服务器端,并且通过一个 session ID(通常是一个 cookie)来标识这个 session。
  • Cookie 加密: secret 参数用于签名 session ID,以防止篡改。它不会加密 session 数据本身。session 数据仍然需要在客户端可见,以便与服务器通信。

2. 如何实现加密存储

如果你希望加密存储 cookie 的内容,可以使用 cookie 包来手动处理 cookie 的加密。以下是一个示例:

const express = require('express');
const cookieParser = require('cookie-parser');
const crypto = require('crypto');

const app = express();

// 生成密钥
const secretKey = 'your-secret-key';

// 自定义加密函数
function encrypt(data) {
    const cipher = crypto.createCipher('aes-256-cbc', secretKey);
    let encrypted = cipher.update(JSON.stringify(data), 'utf8', 'hex');
    encrypted += cipher.final('hex');
    return encrypted;
}

// 自定义解密函数
function decrypt(data) {
    const decipher = crypto.createDecipher('aes-256-cbc', secretKey);
    let decrypted = decipher.update(data, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    return JSON.parse(decrypted);
}

app.use(cookieParser());

app.get('/google', (req, res) => {
    const data = { google: 'google', password: '123456' };
    const encryptedData = encrypt(data);

    // 设置加密后的 cookie
    res.cookie('encryptedData', encryptedData, { httpOnly: true });

    res.send('ok');
});

app.get('/decrypt', (req, res) => {
    const encryptedData = req.cookies.encryptedData;
    const decryptedData = decrypt(encryptedData);

    console.log(decryptedData); // { google: 'google', password: '123456' }

    res.send('decrypted');
});

app.listen(3000, () => {
    console.log('Server is running on port 3000');
});

在这个例子中,我们使用 AES-256-CBC 算法对 cookie 数据进行加密和解密。这样可以确保客户端看不到原始数据。

3. 修改实验的现象

在你的实验中,当你修改 cookie 值时,第二次读取会变成 {} 的原因可能是由于 cookie 的签名验证失败导致的。一旦签名验证失败,Express 会认为该 cookie 已经被篡改,因此将其重置为空对象。

总结来说,如果你希望更优雅地处理加密的 cookie,可以考虑使用上面的自定义加密方法。

回到顶部