我正在构建一个节点/快速应用程序并使用express-session
和mongo-connect
模块存储我的会话并在重新启动时保留它们。
但是,每次重新启动服务器时仍会生成新的Cookie ID。我已将问题缩小到我的会话密码,这是一个随机生成的16个字符串,这是我的会话代码:
app.use(session({
secret: dbops.randomString(16), // generate a 16 random char string
saveUninitialized: false,
resave: true,
store: new MongoStore({
db: thisDb,
ttl: 14 * 24 * 60 * 60
})
}));
问题在于我的randomString
函数 - 当我用静态字符串替换它时,我的会话在服务器重启时仍然存在。我的 cookie秘密应该是什么?我应该选择一个长的随机字符串并存储在ENV变量中吗?
我认为我一般对字符串的用途感到困惑,因为我的Cookie SID似乎是随机生成的。
答案 0 :(得分:14)
典型的会话cookie看起来像这样:
s%3Al3ozSdvQ83TtC5RvJ.CibaQoHtaY0H3QOB1kqR8H2A
它会比这长,但格式大致相同。
s%3A
表示它是已签名的Cookie。l3ozSdvQ83TtC5RvJ
是会话ID(您可以通过检查服务器上的req.session.id
来确认)。CibaQoHtaY0H3QOB1kqR8H2A
是签名。您可以认为secret
有点像用于生成签名的密码
通常,签名用于确认文本来自正确的位置。有人可能会篡改文本,但他们无法使用正确的签名对其进行签名,因为他们不知道secret
。在cookie的上下文中,cookie的“起源”是服务器本身,因此它只是提供了一种方法来确认返回的cookie与发送的cookie相同。
但是,在会话ID的上下文中并不重要,因为如果有人更改了会话cookie,那只是意味着它们将不再登录,因为它与数据库中的id不匹配。那么为什么要打扰他们呢?
生成随机会话ID实际上非常困难。即使它看起来随机,但仍有可能让某人猜测它。签名可以帮助解决这个问题:当“随机”ID不是非常随机时,我们如何阻止某人猜测另一个用户的会话ID?
让我们把它带到一个假设的极端。我们不是使用随机会话ID,而是仅计数,因此第一个会话的标识为1
,下一个会话为2
,依此类推。有人可以很容易地猜出会话ID是什么,但这还不足以劫持会话。他们还需要能够签名,以获得类似的东西:
s%3A432.D5egYRj1G7sJyfbyB7jDh7Gf
此处的会话ID为432
,并且不难猜测,但如果不知道签名,黑客就无法对该知识做任何事情。因此,即使您可以猜出“随机”部分,签名也很难猜测cookie值。
回到关于express-session
的问题,顾名思义secret
需要保密。它需要在重新启动之间保持不变,或者,正如您所注意到的,签名都将变为无效,旧会话cookie将全部被拒绝。群集中的节点之间也需要相同,因为无法保证请求始终会转到同一个节点。
您还应该了解可以使用的keys
设置,而不是secret
。使用keys
可以更改用于生成签名的secret
,而不会立即使所有现有会话无效。我们的想法是指定一组键(秘密)。只有第一个用于生成签名,但所有条目对于检查cookie上的传入签名是有效的。这个想法很简单,只要需要它们就可以将旧秘密包含在阵列中,然后一旦我们确信没有会话正在使用它们就可以删除它们。
我应该选择一个长的随机字符串并将其存储在ENV变量中吗?
差不多。它不一定要疯狂,但确实需要让人猜测。这有点像密码,但有一个优点,你不必记住它。理想情况下,您将生产中使用的secret
保留在代码之外,使用环境变量将是实现此目的的一种方法。