Express-session安全Cookie无效

21

如果不使用安全cookie true设置,我的应用程序用户登录正常。当我启用安全cookie时,登录似乎顺利进行,但似乎未保存cookie并且用户未登录。

换句话说,以下方法可行:

app = express();
app.use(session({
    secret: 'secret code',
    store: sessionStore,
    resave: false,
    saveUninitialized: false,
    cookie: {
        secure: false,
        maxAge: 5184000000 // 60 days
        }
}));

这个无法工作(用户无法登录):

app = express();
app.set('trust proxy');
app.use(session({
    secret: config.cookieSecret,
    store: sessionStore,
    resave: false,
    saveUninitialized: false,
    proxy: true,
    secureProxy: true,
    cookie: {
        secure: true,
        httpOnly: true,
        maxAge: 5184000000 // 60 days
        }
}));

在 Cloudflare 和 Nginx 的后面。这是我的 Nginx 配置:

location ~ / {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://localhost:3000;
}

根据我所读的,我认为它应该可以正常工作。有什么我忽略的地方吗?

编辑:我正在使用有效的SSL证书运行https。


可能是如何使用JavaScript读取http only cookie的重复问题。 - Mike Doe
8个回答

46

对我有效的设置组合:

  • nginx服务器配置中添加proxy_set_header X-Forwarded-Proto $scheme;
  • express-session配置中:

    server.use(
      session({
        proxy: true, // NODE_ENV === 'production'
        cookie: {
          secure: true, // NODE_ENV === 'production'
        },
        // everything else
      })
    );
    

9
在nginx中设置代理头帮助我解决了我的问题。谢谢! - Curious101
1
感谢您的回答。我已经花了最近两天时间来调试这个问题,甚至彻底更改了我的GraphQL客户端,以为那可能是原因。只有当我注意到在生产环境中设置secure为false时,cookie才能正常工作,我才意识到可能是其他问题。 - Frisbetarian-Support Palestine

12

在使用Heroku时的解决方案:

在Heroku中,所有请求都以普通的http形式进入应用程序,但它们有一个头部X-Forwarded-Proto,以了解原始请求是http还是https。这导致Express看到非SSL流量,因此它拒绝在Heroku上运行时设置安全cookie。Express只会通过https发送安全cookie。您需要告诉Express信任X-Forwarded-Proto标头中的信息,即原始请求是通过https进行的,方法是启用“trust proxy”设置。在定义cookie属性之前,我会这样做。

app.set('trust proxy', 1);

其中1代表信任第一个代理。对我来说,1已足够好以设置cookie:secure


这对我托管在 AWS 上的应用程序也适用。 - Haruku

8

Apache反向代理解决方案

对于那些正在寻找潜在解决方案并使用Apache反向代理的用户,请确保在相应域名的VirtualHost指令内,在ProxyPass指令之前添加以下指令:

RequestHeader set X-Forwarded-Proto expr=%{REQUEST_SCHEME}
RequestHeader set X-Forwarded-SSL expr=%{HTTPS}

ProxyPass / http://localhost:8001/
ProxyPassReverse / http://localhost:8001/

这个更改使我的Cookie得以持久保存。

另外,正如其他人指出的那样,不要忘记将以下代码添加到你的生产环境(或使用代理的其他托管环境) express配置中:

  if (app.get('env') === 'production') {
      app.set('trust proxy', 1); // trust first proxy, crucial
  }

最后,确保你的cookie/session设置正确。可以查看Stoyan Borov的回答了解最低要求。以下是我如何进行设置:

    app.use(session({
        cookie: {
            sameSite: 'lax', // lax or strict
            secure: process.env.NODE_ENV === 'production', // Crucial
            maxAge: 1000 * 60 * 60 * 24 * 30, // 30 days
        },
        proxy: true, // Crucial
        resave: false,
        saveUninitialized: true,
        secret: 'Gotta love cookies',
        store: yourSessionStore,
        rolling: true,
    }));

非常好的答案,感谢对Apache步骤的清晰解释! - gudbrand3
嗨,维克多,我遇到了同样的问题。在这里看看 https://dev59.com/tLP3oIgBc1ULPQZFcGca。你能告诉我如何在 nodejs/express 中设置你提到的 apache 头吗?我无法使用 apache 设置它们,因为 heroku 不使用它。 - Chris Hansen
请记住启用头部模块:sudo a2enmod headers - anthonygore

2
这正是安全cookie所做的。它不会在不安全的环境中被浏览器保存,例如http://。您需要添加一个ssl证书,将所有http请求重定向到https,然后cookie就可以在浏览器中保存了。在本地设置https很麻烦,因此在配置/环境变量中设置secure为false,并在源代码控制中启用prod/staging。另外,还要启用resave,resave: true

1
我正在使用有效的SSL证书进行HTTPS连接。抱歉,我应该在之前就提供这个信息了。 - Justin Lok
1
启用重新保存,resave: true - Swaraj Giri

2

我猜问题的实际是这样的:

httpOnly: true

这意味着任何客户端代码都无法访问 cookie(通过 document.cookie),并且您执行的任何 XHR(“AJAX”)请求在发送请求之前都需要显式设置 withCredentials,才会发送任何 cookie。

具体取决于您使用的客户端设置,如何进行设置:


你好,您知道如何回答这个问题吗?http://stackoverflow.com/questions/44041506/how-to-check-that-a-user-has-already-voted-on-a-post - Coder1000

0
从cookie-parser中删除密钥似乎可以解决问题并允许登录。我使用的是与会话cookie相同的密钥(正如文档所说),所以我不明白为什么它不起作用,但在使用不安全的cookie时却起作用了。将密钥保留在cookie-parser中并启用会话重新保存也可以工作。如果有人能解释一下这个问题,那就太好了。

0

app.set('trust proxy', 1); 运行得非常顺利


0
<VirtualHost *:80>
 ServerName xx.com
    ProxyPreserveHost On
<Location "/">

        ProxyPreserveHost On
        ProxyPass http://localhost:8080/
        ProxyPassReverse http://localhost:8080/
        RequestHeader set X-Forwarded-Port "443"
        RequestHeader set X-Forwarded-Proto "https"
</Location></VirtualHost>



网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接