通过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
用作此唯一标识符的键。现在如果我们发送一个请求,我们可以看到类似的情况。
会话中间件设置新的会话标识符
浏览器现在会自动设置并存储这个 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 攻击。
合理的会话超时时间会话过期时间应符合您的应用程序需求,如果您更注重安全性,则应较短,反之则较长。
正确实现安全退出在注销时,你必须确保会话和相关数据被彻底清除。不然的话,这些会话可能会在注销后被继续使用。(仅从客户端浏览器清除 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共同学习,写下你的评论
评论加载中...
作者其他优质文章