为了账号安全,请及时绑定邮箱和手机立即绑定

什么是会话固定漏洞以及如何在Node.js中防止这种攻击

标签:
安全

通过Session Fixation攻击,攻击者可以劫持一个有效的用户会话,因此,了解此漏洞以及如何防范非常重要。

会话固定攻击以及如何修复它

在开始之前,我们需要知道什么是会话过程以及验证会话是如何工作的。如果你已经熟悉这些内容,可以直接跳到 什么是会话固定 来了解或 如何防止会话固定

什么是会话?

我们知道HTTP请求是无状态的,这意味着当我们发送登录请求时,而且用户名和密码有效,默认情况下没有任何机制知道我是发出下一个请求的同一个人。为了解决这个问题,换句话说,让请求具有状态,有一些建议的方法,如Cookies隐藏表单域URL参数HTML5本地存储JWT和Session。在这篇文章中,我们将重点讨论会话(Session)。

会话数据 是存储在服务器上的数据。每个客户端都会获得一个与服务器数据唯一标识符 相关联的唯一标识符。客户端必须在每个请求中发送这个唯一标识符,这样我们就能知道是谁发的请求。这个标识符可以放在 cookie 或 URL 参数里。

一个简单的示例,展示 expressjs 应用里的会话(session)和标识符(sessionId)。

    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('服务器已在端口3000运行');
    });

当第一次发送请求时,express-session 中间件会创建一个新的唯一标识符,并将该标识符作为 cookie 存储在某个地方(例如内存,但我们也可以使用自定义存储方式)。在会话中间件的选项中,我们将 sessionId 用作此唯一标识符的键。现在如果我们发送一个请求,我们可以看到类似的情况。

Session Middleware Assinging a new Session Id

会话中间件设置新的会话标识符

浏览器现在会自动设置并存储这个 cookie,以便于后续的请求。如果我们发送的请求包含一个有效的会话(如果会话存在于我们的内存中),我们不会在响应中看到 Set-Cookie 头:

当存在有效会话时,不再设置新的Cookie。

当用户登录后,我们可以将用户信息存储在序列化编码的 cookie 中,或者将其存储在数据库中,并与 sessionId(会话ID)关联。我们来用一个 Map 当作数据库。

    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('再试一下');  
        }  
    });

如果我们登录了,再用 cookie 发送一个对 /me 的请求,就可以看到这样的结果了。

与该会话ID相关的用户数据

这是一段简化的总结,解释了我们为什么必须使用会话以及如何实现。

攻击者能不能创建一个有效的会话标识?

在这种情况下,当我们使用express-session时,可以看到我们向session中间件传递了一个secret。secret是用来签署cookie值的。这意味着我们能确保我们生成了sessionId。因此,只要确保您向客户端发送的是经过签署的值,就不可能被别人篡改。

会话示例。
sessionId=s%3AL6j4T8hBwMk1ulJqGoisZbAxUOkOuQqP.x5UxPQEtKrj3sWrIy6S01CQRjAtp4biVs4H2zgqmSs

首先,s%3A 简单来说,这意味着:s: 这个前缀表示我们的 cookie 会话已经签名!

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

第三部分:这是签名部分:x5UxPQEtKrj3sWrIy6S01CQRjAtp4biVs4H2zgqmSs。我们用内部密钥生成了这个字符串,所以这个 cookie 是我们自己弄出来的。

我们可以简单地重新生成这个签名(这里指的应该是数字签名或验证代码),并验证其有效性,如下:

    const crypto = require('crypto'); 
    const secret = 'secret'; // 密钥
    const sessionId = 'L6j4T8hBwMk1ulJqGoisZbAxUOkOuQqP'; // 会话ID
    const hmac = crypto.createHmac('sha256', secret); // 创建HMAC对象
    hmac.update(sessionId); // 更新
    const signature = hmac.digest('base64').replace(/\=+$/, ''); // 生成摘要并移除末尾的等于号
    console.log(signature); // 生成的签名是: x5UxPQEtKrj3sWrIy6S01CQRjAtp4biVs4H2zgqmSs

这是express-session检查的方法。

什么是会话固定攻击?

在会话固定攻击中,攻击者劫持了有效的用户会话。我们说过会为 cookie 加密签名,以确保不会有人冒用其他用户的有效会话。但是,如果攻击者有自己有效的会话,试图将其和另一个用户的会话关联起来呢?这样,他就可以冒充受害者进行操作。

问题出现于不生成新的session ID的操作,比如登录时。

攻击者是怎么干成的?

其中一个情况是攻击者有物理访问计算机。作为攻击者,我去到大学并选择一台共享电脑,然后我在 vulnerablewebsite.com 上登录我的账户,然后不注销(这通常会销毁服务器中的会话),我留下一个在 vulnerablewebsite.com 上打开的登录页面,并且在此之前我复制了我的有效 sessionId。现在受害者正在使用这台电脑,如果受害者登录,攻击者的 sessionId 就会与受害者的账户关联。听起来是不是有点复杂?实际上很简单,让我们来看看实际操作:

当受害者登录时,攻击者的 sessionId 会与他们的账户关联。

让我们用第一个用户Bob(这个用户)来登录,

现在浏览器现在为这个网站设置了这个 cookie。这意味着如果其他人尝试发送登录请求的话,express-session 不会再生成新的 sessionId,它会直接覆盖掉当前的 sessionId

让我们想象小乔(受害者)决定用这台共享电脑,鲍勃的 cookie 和有效的会话数据也会被发送。

使用现有的有效会话令牌进行登录

我们没有得到新的session或 cookie!

魔术生效了,现在 Bob 的 sessionId 和 Joe 的用户关联起来了。因此,如果恶意攻击者 Bob 发送一个对 /me 的请求,他就会收到 Joe 的数据:

使用 Bob 的会话 ID 来获取与 Joe 相关的数据

我们通过Bob的会话获取了Joe的资料。在这个例子中,攻击者进行了物理接触,但是如果没有物理接触,只要存在其他漏洞,比如XSS,也有可能做到同样的事情。

一些网站在请求中通过 URL 参数传递 sessionId。在这种情况下,如果攻击者在登录页面的链接中使用自己的 sessionId,就可以被攻击者利用。

通过URL参数实施会话固定攻击

更多关于此方法的安全挑战的信息,请查看this stack exchange question

怎么防止会话固定攻击呢?
登录时,生成新会话!

主要的解决办法真的很简单,这样做你就不用担心会出现会话覆盖的情况了。

让我们修改我们的代码:

    app.post('/login', (req, res) => {  
        const { name } = req.body;  
        req.session.regenerate(err => {  
            if (err) {  
                res.send('出错啦');  
            } else {  
                const user = users.find(u => u.name === name);  
                if (user) {  
                    db.set(req.sessionID, user);  
                    res.send('成功啦');  
                } else {  
                    res.send('再试一次吧');  
                }  
            }  
        });  
    });

我们可以使用 regenerate 函数,每次有人登录时都会分配一个新的会话,无论你是否传递了会话 cookie,都会生成一个新的会话 ID 并通过 Set-Cookie 头将其发送给客户端。

每次登录时都会获得一个新的 sessionId 和 Set-Cookie 头

仅使用 HttpOnly 类型的 Cookies

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

防止XSS攻击

会话固定攻击可以与跨站脚本(XSS)攻击结合使用,从而变得更加有效。如果你担心会话固定,确实也需要重视 XSS 攻击。

合理的会话超时时间

会话过期时间应符合您的应用程序需求,如果您更注重安全性,则应较短,反之则较长。

正确实现安全退出

在注销时,你必须确保会话和相关数据被彻底清除。不然的话,这些会话可能会在注销后被继续使用。(仅从客户端浏览器清除 cookie 是不够的!)

Passport.js 是否容易被会话固定攻击?

是的,在0.6.0版本之前确实存在这个问题,Passport的维护者认为会话重置应该在应用层面完成,一段时间后,他们意识到这个问题的重要性,并在0.6.0版本中修复了它。如果你想了解更多关于这次修复的信息,你可以在这里阅读更多详情。

结论:

会话固定漏洞可能发生在用其他用户的sessionId覆盖现有的sessionId的情况下。解决方法如下,每次用户登录时生成一个新的会话,并使用HTTP-only cookies,设置合适的过期时间以及正确的注销过程。

参考资料

https://owasp.org/www-community/attacks/Session_fixation# 会话固定攻击 (#)

点击这里查看有关会话固定攻击的信息:https://developer.mozilla.org/en-US/docs/Web/Security/Types_of_attacks#session_fixation (会话固定攻击的类型)

[OWASP Cheat Sheet 系列 - 会话管理,该项目的全部 Cheat Sheet 集合页面。](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html?source=post_page-----03580b6acd67--------------------------------#在任何权限级别更改后刷新会话 ID)
保护您的用户免受会话固定不安全地处理会话ID可能会使您的用户容易受到会话劫持。准备看看发生了什么吗? 更多详情请点击…www.hacksplaining.com
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消