什么是会话固定以及如何在 Node.js 中防止它

什么是会话固定以及如何在 Node.js 中防止它

在深入讨论之前,我们需要了解会话是什么以及会话身份验证如何工作。

什么是会话?

正如我们所知,HTTP 请求是无状态的,这意味着当我们发送登录请求时,并且我们有有效的用户名和密码,没有默认机制可以知道我们是发送下一个请求的同一个人。为了解决这个问题,换句话说,使请求有状态,有一些常用的方法,如Cookie、隐藏表单字段、URL 参数、浏览器存储、JWTSession。在本文中,我们将重点关注Session

Session是存储在服务器上的数据。每个客户端都会获得一个与服务器上的数据相关的唯一标识符 。客户端必须在每个请求上发送此唯一标识符,以便我们知道谁发送了此请求。该标识符可以在 cookieURL 参数中发送。

下面是使用express-sessionexpress创建session的简单示例:

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进行绑定. 让我们使用 Map 作为我们的简单数据库:

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: 'leo', 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');
    }
});

攻击者可以创建有效的会话 ID 吗?

在本例中,我们使用的是express-session。可以看到我们向express-session中间件传递了一个secret值。这个secret用于签署我们的 cookie 的值。它只是意味着我们确信是我们生成了sessionId

session示例:

sessionId=s%3AL6j4T8hBwMk1ulJqGoisZbAxUOkOuQqP.x5UxPQEtKrj3sWrIy6S01CQRjAtp4biVs4H2zgqmSs

第一部分:s%3A简单来说:s:这是一个前缀,表明我们的 cookie 会话已签名!

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

第三部分:x5UxPQEtKrj3sWrIy6S01CQRjAtp4biVs4H2zgqmSs这是签字部分。我们使用我们的secret生成了此文本,因此我们可以确定此 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

什么是会话固定?

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

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

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

攻击者如何才能做到这一点?

其中一种情况是攻击者可以物理访问计算机。作为攻击者,我们可以选择一台共享计算机,打开某个已经登录过的网页,然后复制session后发送请求。

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并将其发送到客户端中。

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

防范 XSS

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

合理的会话过期时间

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

正确的注销实施

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

相关推荐
真滴book理喻1 小时前
Vue(四)
前端·javascript·vue.js
程序员_三木1 小时前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
开心工作室_kaic3 小时前
springboot476基于vue篮球联盟管理系统(论文+源码)_kaic
前端·javascript·vue.js
川石教育3 小时前
Vue前端开发-缓存优化
前端·javascript·vue.js·缓存·前端框架·vue·数据缓存
搏博3 小时前
使用Vue创建前后端分离项目的过程(前端部分)
前端·javascript·vue.js
温轻舟3 小时前
前端开发 之 12个鼠标交互特效上【附完整源码】
开发语言·前端·javascript·css·html·交互·温轻舟
web135085886353 小时前
2024-05-18 前端模块化开发——ESModule模块化
开发语言·前端·javascript
LCG元4 小时前
javascript页面设计案例【使用HTML、CSS和JavaScript创建一个基本的互动网页】
javascript
技术程序猿华锋5 小时前
Gemini 2.0 Flash 体验版实测:日常视觉识别的最佳选择,关键在于其API Key现在是免费调用
开发语言·javascript·ecmascript·googlecloud·gemini
TttHhhYy5 小时前
uniapp+vue开发app,蓝牙连接,蓝牙接收文件保存到手机特定文件夹,从手机特定目录(可自定义),读取文件内容,这篇首先说如何读取,手机目录如何寻找
开发语言·前端·javascript·vue.js·uni-app