「译」 什么是会话固定「Session Fixation」以及如何在 Node.js 程序中进行预防

原文:levelup.gitconnected.com/what-is-ses...

标题:What is Session Fixation and How to Prevent it in Node.js

作者:Poorshad Shaddel

通过会话固定Session Fixation ,攻击者可以劫持有效的用户会话,因此了解此漏洞并防范它绝对重要。

在深入研究之前,我们需要知道Session是什么以及会话身份验证Session Authentication的工作原理。如果你已经熟悉这一点,则可以跳到该部分:什么是会话固定_及_如何防止会话固定

什么是会话?

众所周知,HTTP请求是无状态的,这意味着当我们发送登录请求时,我们有一个有效的用户名和密码,没有默认机制来知道我与发送下一个请求的是同一个人。为了解决这个问题,我们需要使请求是有状态的,常见的方法,如 Cookie隐藏表单字段URL 参数HTML5 Web 存储JWT 和会话。在本文中,我们将重点介绍Session。

Session 是存储在服务器上的数据。每个客户端都有一个与服务器上的此数据关联 的唯一标识符。客户端必须在每个请求上发送此唯一标识符,以便我们知道谁在发送此请求。此标识符可以在 Cookie 或 URL 参数中携带。

expressjs 应用程序中显示会话和标识符 (sessionId) 的简化示例:

js 复制代码
const app = require('express')();
const session = require('express-session');
app.use(require('cookie-parser')());
app.use(require('body-parser').json());

app.use(session({
    secret: 'secret',
    cookie: { maxAge: 60000 },
    name: 'sessionId'
}));

app.get('/', (req, res) => {
    res.send('ping');
});

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

当第一次发送请求时,express-session 中间件会创建一个新的唯一标识符,并将其设置为 cookie,同时将其存储在某个地方(在本例中为内存,但我们也可以传递给我们自定义的存储系统)。 在会话中间件的选项中,我们使用 sessionId 作为存储此唯一标识符的密钥的名称。现在,如果我们发送一个请求,我们会看到如下内容:

浏览器现在设置此 cookie 并自动存储以备进一步请求。如果我们发送一个包含有效会话的请求(该会话存在于我们的会话存储中 - 在我们的例子中是内存),我们不会在响应中返回 Set-Cookie 标头:

当用户登录时,我们可以将用户信息存储在序列化的 cookie中,也可以将其存储在数据库中并将数据与 sessionId 关联,以映射至我们的数据库:

js 复制代码
const db = new Map();
app.get('/me', (req, res) => {
    const user = db.get(req.sessionID);
    res.json({ mySessionId: req.sessionID, me: user ? user : 'anonymous' });
});
const users = [{ name: 'bob', age: 19 }, { name: 'joe', age: 20 }];
app.post('/login', (req, res) => {
    const { name } = req.body;
    const user = users.find(u => u.name === name);
    if (user) {
        db.set(req.sessionID, user);
        res.send('ok');
    } else {
        res.send('try again');
    }
});

如果我们登录,然后使用 cookie 向 /me 发送另一个请求,我们会得到以下结果:

这是对为什么我们必须使用session以及如何做到这一点的简化总结。

攻击者能否创建有效的会话 ID?

在这种情况下,我们使用的是 express-session 。我们将一个密钥传递给了会话中间件。此密钥用于签署我们 cookie 的值。它只是意味着我们确定是我们生成了 sessionId。因此,只要你向客户端发送签名值,就不可能。

session示例:

sessionId=s%3AL6j4T8hBwMk1ulJqGoisZbAxUOkOuQqP.x5UxPQEtKrj3sWrIy6S01CQRjAtp4biVs4H2zgqmSs

第一部分:s%3A 的意思是:s:这是一个前缀,表示我们的 cookie 会话已签名!

第二部分:L6j4T8hBwMk1ulJqGoisZbAxUOkOuQqP 这是我们的 sessionId,我们在数据库中使用它来关联数据。

第三部分:这是第三部分:x5UxPQEtKrj3sWrIy6S01CQRjAtp4biVs4H2zgqmSs 这是签名部分。我们使用我们的秘密生成了此文本,因此我们可以确定此cookie是由我们生成的。

我们可以简单地重新生成这个符号并检查它是否有效:

js 复制代码
const crypto = require('crypto');
const secret = 'secret';
const sessionId = 'L6j4T8hBwMk1ulJqGoisZbAxUOkOuQqP';
const hmac = crypto.createHmac('sha256', secret);
hmac.update(sessionId);
const signature = hmac.digest('base64').replace(/\=+$/, '');
console.log(signature); // x5UxPQEtKrj3sWrIy6S01CQRjAtp4biVs4H2zgqmSs

这就是 express-session 检查它的方式。

什么是会话固定 Session Fixation?

在会话固定攻击中,攻击者劫持有效的用户会话。我们说我们签署cookie是为了确保没有人可以劫持其他用户的有效会话。但是,如果攻击者有自己的有效会话并尝试将其与其他用户关联,该怎么办?在这种情况下,他可以代表受害者采取行动。

当我们没有在登录等操作上生成新的 sessionIds(唯一标识符)时,就会出现问题

攻击者如何做到这一点?

其中一种情况是攻击者对计算机具有物理访问权限。作为攻击者,我去大学,选择其中一台共享计算机,然后在 vulnerablewebsite.com 上登录我的帐户,然后不进行注销(这通常会破坏服务器存储中的会话),我在 vulnerablewebsite.com 上留下一个打开的登录页面,在此之前,我必须复制我的有效sessionId。现在受害者正在使用这台计算机,如果受害者登录,攻击者 sessionId 将与受害者的帐户相关联。听起来很复杂?一点也不,让我们看看实际情况:

让我们使用我们的第一个用户 Bob(攻击者)登录:

现在,浏览器为本网站设置了此cookie。这意味着,如果其他人尝试发送登录请求,express-session 不会生成新的 sessionId,而是**覆盖现有的 *sessionId

假设 Joe (受害者)决定使用这台共享计算机,也会发送 Bob 的 cookie 和有效会话

我们没有收到新的会话或cookie!

奇迹发生了,现在 Bob 的 sessionId 与 Joe 的用户相关联。因此,如果攻击者 (Bob) 向 /me 发送请求,他将返回 Joe 的数据:

我们能够通过使用 Bob 的会话来获取 Joe Data。在此示例中,攻击者具有物理访问权限,但如果存在其他一些漏洞(例如 XSS),则可以在没有物理访问权限的情况下执行此操作。

某些网站在请求中将 sessionId 作为 URL 参数传递。在这种情况下,如果攻击者在 URL 参数上提供带有其 sessionId 的登录页面链接,则有可能被利用。

在此堆栈交换问题中阅读有关此方法的安全挑战的更多信息。

如何防止会话固定?

登录时生成新会话!

主要解决方案非常简单,通过这样做,始终可以确定不会发生此会话覆盖!

让我们更改代码:

js 复制代码
app.post('/login', (req, res) => {
    const { name } = req.body;
    req.session.regenerate(err => {
        if (err) {
            res.send('error');
        } else {
            const user = users.find(u => u.name === name);
            if (user) {
                db.set(req.sessionID, user);
                res.send('ok');
            } else {
                res.send('try again');
            }
        }
    });
});

我们可以使用regenerate函数,以便在每次有人想要登录时分配一个新会话。是否传递会话 cookie 不再重要,它将生成一个新的会话 ID 并将其发送到 Set-Cookie 标头中的客户端。

当你使用 HTTP Only 时,这意味着只有服务器可以通过 Set-Cookie 标头设置 cookie,而客户端(浏览器 JavaScript)无法更改它。因此,即使你的应用存在 XSS 漏洞,攻击者也无法更改 sessionId (cookie)。

防范 XSS

会话固定可以与 XSS 攻击结合使用以更有效,因此如果你担心会话固定,那么认真对待 XSS 攻击确实是有意义的。

合理的会话到期时间

会话过期时间应符合应用程序的特定要求,如果你更关心安全性,则应更短,反之亦然。

正确的注销实现方案

注销时,你必须正确销毁现有会话及其与任何数据的关联。否则,这些会话可以在注销后使用。(从客户端浏览器中删除cookie是不够的!

Passportjs 是否容易受到会话固定的影响?

是的,在 0.6.0 之前的版本中,问题就在那里,Passport 维护者认为会话重新生成应该在应用程序端完成,但一段时间后他们意识到问题的重要性,并在 0.6.0 版本中修复了它。如果你对此修复程序的详细信息感兴趣,可以在此处阅读所有详细信息。

结论

如果用其他用户数据覆盖现有 sessionId,则可能会发生会话固定。解决方案非常简单,每次有人登录时都会生成一个新会话,使用仅限 HTTP 的 cookie、适当的过期时间、正确的注销实现。

References

owasp.org/www-communi...

developer.mozilla.org/en-US/docs/...

相关推荐
前端李易安10 分钟前
什么是HTTP,什么是HTTPS?HTTP和HTTPS都有哪些区别?
网络协议·http·https
IT小辉同学11 分钟前
一键生成本地SSL证书:打造HTTPS安全环境
安全·https·ssl
胎粉仔11 分钟前
网络初阶——应用层:HTTPS 协议
网络协议·http·https
weisian15116 分钟前
认证鉴权框架SpringSecurity-2--重点组件和过滤器链篇
java·安全
Koi慢热25 分钟前
信息收集合集
网络·安全·web安全·网络安全
hgdlip1 小时前
本机ip地址和网络ip地址一样吗
网络·网络协议·tcp/ip·网络ip地址·本机ip地址
爱编程的鱼2 小时前
Node.js事件循环:解锁异步编程的奥秘
node.js
南暮思鸢2 小时前
Node.js is Web Scale
经验分享·web安全·网络安全·node.js·ctf题目·hackergame 2024
程序员小杰@3 小时前
Playwright 快速入门:Playwright 是一个用于浏览器自动化测试的 Node.js 库
node.js
Martin -Tang3 小时前
vite和webpack的区别
前端·webpack·node.js·vite