避免存储用于访问公共页面的 cookie

Posted

技术标签:

【中文标题】避免存储用于访问公共页面的 cookie【英文标题】:Avoid storing cookies for visits to public pages 【发布时间】:2022-01-09 19:17:29 【问题描述】:

根据Log user out of previous sessions,我使用 NodeJS 和 express-session 将会话信息存储在数据库中。相关代码在主脚本文件中:

const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);

// Initialize mongodb session storage to remember users.
const store = new MongoDBStore(
  uri: config.mongoUri,
  // The 'expires' option specifies how long after the last time this session was used should the session be deleted.
  // Effectively this logs out inactive users without really notifying the user. The next time they attempt to
  // perform an authenticated action they will get an error. This is currently set to 3 months (in milliseconds).
  expires: max_session_ms,
);


// Enable sessions using encrypted cookies
app.use(cookieParser(config.secret));
app.use(
  session(
    cookie: 
      // Specifies how long the user's browser should keep their cookie, probably should match session expiration.
      maxAge: max_session_ms
    ,
    store: store,
    secret: config.secret,
    signed: true,
    resave: true,
    saveUninitialized: true,
    httpOnly: true,  // Don't let browser javascript access cookies.
    secure: config.secureCookies, // Only use cookies over https in production.
  )
);

在路由文件中:

passport.use('user-otp', new CustomStrategy(
  // code to validate one-time password.
));

问题在于,这种方法还会在浏览器上为未登录的用户存储 cookie。以下是访问公共页面的示例:

我只想在创建帐户或登录后(用户同意 cookie 政策)存储 cookie,而不是在公共页面上存储 cookie,以避免 GDPR 要求的“cookie 同意框”。

如何仅在浏览器登录后存储 cookie?

更新

我按照答案中的建议删除了两个cookie。我停止服务器,消除localhost 的cookies,启动服务器,发出公共页面请求,公共页面上还有一个cookie。这是服务器数据库上的会话数据:

> db.sessions.find().pretty()

    "_id" : "DObp-FFNJGLD5c5kLKWfkCaEhfWHtWpo",
    "expires" : ISODate("2022-03-03T19:41:29.807Z"),
    "session" : 
        "cookie" : 
            "originalMaxAge" : 7776000000,
            "expires" : ISODate("2022-03-03T19:41:29.807Z"),
            "secure" : null,
            "httpOnly" : true,
            "domain" : null,
            "path" : "/",
            "sameSite" : null
        ,
        "flash" : 
            
        
    

浏览器会显示这个 cookie:

当我从应用程序中删除 PassportJS 以及将 sameSite 设置为 "strict" 时,我得到了相同的结果。

第二次更新

由于数据库会话有一个空的flash 字段,我怀疑这是由于闪现消息。我删除了这段代码:

const flash = require("express-flash");
app.use(flash());

现在服务器不存储用于访问公共页面的 cookie。我在通知中为登录用户和公共页面访问者使用 Flash 消息,例如当页面不再可用时。

所以问题变成了:是否可以仅在服务器端使用闪存消息,从一个处理程序到另一个处理程序,而不存储 cookie?

【问题讨论】:

【参考方案1】:

在创建静态页面路由之后为 cookie/会话配置您的 app.use()。 Express 按照声明的顺序评估中间件和路由。

基本上是这样做的:

// Create express app

// Declare app.use(express.static) routes

// Declare app.use(cookie) and app.use(session) routes

// Declare routes that need to use cookies/sessions

因为在这种情况下,静态内容路由是在 cookie/会话中间件之前声明的,所以 cookie 中间件不会应用于静态路由。

如果您的路由结构比这更复杂,我建议您将会话逻辑设置为可以从专用文件(middlware.js?)导出的中间件函数。然后,您可以要求它并根据需要在特定路由器中添加 cookie/会话中间件(在该路由器中声明任何静态路径之后)。

【讨论】:

是的,这行得通。一个缺点:如果用户访问公共页面,它看起来好像他们已退出,例如即使发送了 cookie,他们也会看到“登录”按钮。对于我的用例,我不需要同意,因为我只会存储技术 cookie,例如闪存消息。见***.com/questions/70237367/… 但如果我从头开始,我会这样做,并在子域中实现用户区。【参考方案2】:

离开文档,saveUninitialized 标志应该设置为false -

强制将“未初始化”的会话保存到存储中。当一个会话是新的但未被修改时,它是未初始化的。 选择 false 有助于实现登录会话、减少服务器存储使用或遵守在设置 cookie 之前需要许可的法律。 选择 false 还有助于解决竞争条件,其中客户端在没有会话的情况下发出多个并行请求。 默认值为 true,但不推荐使用默认值, 因为默认值将来会改变。请对此进行研究 设置并选择适合您的用例的内容。

https://github.com/expressjs/session#saveuninitialized

【讨论】:

这消除了公共页面的两个 cookie,我仍然有一个,connect.sid。你也有吗? 嗯,这看起来很奇怪,但可能与resave 标志有关。不太清楚它是如何运作的 - github.com/expressjs/session#resave 我阅读了链接并设置了resave: false。我清除了localhost 的cookies 并重新启动了服务器。我仍然得到localhost 设置的connect.sid cookie。 我刚刚在saveUninitialized 描述的底部看到了这个注释 - Note if you are using Session in conjunction with PassportJS, Passport will add an empty Passport object to the session for use after a user is authenticated, which will be treated as a modification to the session, causing it to be saved. This has been fixed in PassportJS 0.3.0 您可以通过暂时禁用它来测试它是否与 Passport 相关? 我在app.js 中用passport 注释了三行:const passport = require('passport');app.use(passport.initialize());app.use(passport.session());。我仍然得到相同的结果。

以上是关于避免存储用于访问公共页面的 cookie的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript Cookie

JavaScript相关, Cookie

JavaScript相关, Cookie

cookie解析

cookie储存

Cookie学习