所有文章 > 技术杂货铺 > Node.js破损访问控制指南:示例与预防
Node.js破损访问控制指南:示例与预防

Node.js破损访问控制指南:示例与预防

在本文中,我们将讨论访问控制主题以及如何为应用程序提供强大的安全级别。 

首先,我们简要定义损坏的访问控制。然后,我们举例说明损坏的访问控制是什么样子的,以及它们针对哪些漏洞。最后,我们针对这些漏洞提供了一些缓解解决方案。 

如果您没有使用 Node.js 的经验,我们强烈建议您浏览一下Node.js 的简介;除非您具备该技术的背景知识,否则我们将要讨论的某些方面可能不会立即清晰。 

解决了这个问题之后,我们就开始吧。

访问控制 101

简单来说,访问控制描述了一组为监督和控制系统资源访问而实施的策略和机制。您可能知道访问控制的另一个名字:授权。 

一旦服务器通过某种登录或身份验证机制确定了您的身份,它就会授予或限制您在系统内可以访问的功能和资源。此外,我们通常使用访问控制来跟踪用户。 

尽管概念简单,但适当地实施一个强大且安全的访问控制系统却非常困难。访问控制与系统架构紧密相关。此外,用户经常会扮演多个角色,这使得访问管理更加复杂。因此,一般来说,我们不鼓励从头开始开发访问控制,而是建议采用久经考验的强大解决方案,例如OAuth 2.0JWT

NodeJS 损坏的访问控制指南:示例和预防图像

解释损坏的访问控制

破坏的访问控制包含一组已知漏洞,可能对您的系统对资源访问的控制构成威胁。 

尽管许多访问控制漏洞如果被忽视很容易被利用,但你可以相对快速地解决它们。这一点很重要,因为访问控制被破坏的后果可能非常具有破坏性——攻击者基本上可以接管你的系统。

常见的访问控制漏洞

正如我们之前的文章所提到的,攻击者可以利用多种漏洞,具体取决于您的技术堆栈和架构。但是,在本文中,为了简洁起见,我们仅关注不安全的 ID、路径遍历、文件权限和客户端缓存。 

下面让我们重新认识一下访问控制的常见漏洞。 

不安全身份漏洞

大多数现代网站都使用某种形式的 ID 或索引来快速高效地引用数据。在大多数情况下,这种方法效果令人满意。但是,如果 ID 很容易被破解,无论是手动破解还是暴力破解,那么你就有安全问题了。

为了说明这一点,假设您有一个个人资料页面部分,其中显示用户个人资料。

现在,如果我手动更改查询字符串中的数字,并且没有主动访问控制来验证我的请求,那么我就可以访问任何配置文件。

路径遍历漏洞

路径遍历的概念定义了用户自由浏览文件系统目录树的能力。  

缺乏适当访问控制的系统可能会成为路径遍历漏洞的受害者,并允许攻击者访问服务器中受限的资源。 

当系统允许用户检索资源路径,攻击者修改资源路径,而系统未充分验证修改时,就会发生这种情况。 
如果攻击者将user.png更改为类似../../etc/passwd 的内容,他们就可以访问应用程序的秘密。

文件权限漏洞

文件权限漏洞与授予特定用户访问服务器上特定文件的权限机制中的弱点有关。 

所有 Web 应用程序都包含关键配置文件和资源,这些文件和资源应保密,不向用户开放。然而,当应用程序缺乏适当的配置策略时,攻击者就可以攻击这些文件并控制整个平台。 

客户端缓存漏洞

客户端缓存漏洞很难解决,因为它们需要攻击者物理接管用户的计算机。然而,恶意行为者不是远程攻击,而是利用会话凭据、缓存页面或已存在于经过身份验证的客户端浏览器中的数据。一旦发生这种情况,攻击者就已攻陷服务器并可以访问用户数据。游戏结束。

解决访问控制问题

不幸的是,Node.js 没有提供原生方式来实现强大的 Web 应用程序身份验证系统。因此,我们需要自己实现它。在这种情况下,我们将使用Auth0来实现身份验证,作为具有强大且可靠的第三方解决方案的 单点登录服务SSO 服务) 。此外,我们将使用Passport作为我们的中间件库来处理身份验证和授权过程。 

进入 Auth0 仪表板后,转到“应用程序”部分并单击“创建应用程序”按钮。输入应用程序的名称,并确保选择常规 Web 应用程序作为应用程序类型。 

最后,单击“创建”按钮。 

创建应用程序后,转到“设置”选项卡并获取您的 Auth0客户端 ID。然后,将允许的回调 URL设置为http://localhost:3000/oauth2/redirect将允许的注销 URL设置为http://localhost:3000/

第一个 URL 告诉 Auth0 在身份验证后将用户重定向到哪里,第二个 URL 告诉 Auth0 在注销后将用户重定向到哪里。 

最后,保存您的更改。 

实现身份验证中间件

现在,在您的项目中,转到您的.env文件(如果您还没有, 请使用touch .env命令创建一个)并添加以下键。

AUTH0_DOMAIN=__INSERT_DOMAIN_HERE__
AUTH0_CLIENT_ID=__INSERT_CLIENT_ID_HERE__
AUTH0_CLIENT_SECRET=__INSERT_CLIENT_SECRET_HERE__

完成后,继续使用以下命令安装护照中间件:

$ npm install passport
$ npm install passport-openidconnect
$ npm install express-session
$ npm install connect-sqlite3

接下来,在routes 文件夹中创建一个名为auth.js的类文件,并添加以下代码:

var OpenIDConnectStrategy = require('passport-openidconnect');

passport.use(new OpenIDConnectStrategy({
issuer: 'https://' + process.env['AUTH0_DOMAIN'] + '/',
authorizationURL: 'https://' + process.env['AUTH0_DOMAIN'] + '/authorize',
tokenURL: 'https://' + process.env['AUTH0_DOMAIN'] + '/oauth/token',
userInfoURL: 'https://' + process.env['AUTH0_DOMAIN'] + '/userinfo',
clientID: process.env['AUTH0_CLIENT_ID'],
clientSecret: process.env['AUTH0_CLIENT_SECRET'],
callbackURL: '/oauth2/redirect',
scope: [ 'profile' ]
},
function verify(issuer, profile, cb) {
return cb(null, profile);
}));

passport.serializeUser(function(user, cb) {
process.nextTick(function() {
cb(null, { id: user.id, username: user.username, name: user.displayName });
});
});

passport.deserializeUser(function(user, cb) {
process.nextTick(function() {
return cb(null, user);
});
});

var express = require('express');
var qs = require('querystring');
var router = express.Router();

router.get('/login', passport.authenticate('openidconnect'));

router.get('/oauth2/redirect', passport.authenticate('openidconnect', {
successRedirect: '/',
failureRedirect: '/login'
}));

module.exports = router;

当用户点击登录按钮时,系统会将其重定向到由 Auth0 托管的应用登录页面,用户将在此登录。 

登录后,Auth0 会将用户重定向到您的应用程序。 

认证路线

现在,我们需要将身份验证路由添加到我们的应用程序。 

进入app.js类,修改代码如下: 

var indexRouter = require('./routes/index');
var authRouter = require('./routes/auth');

var logger = require('morgan');
var session = require('express-session');
var passport = require('passport');

var SQLiteStore = require('connect-sqlite3')(session);

///...

app.use('/', indexRouter);
app.use('/', authRouter);

app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false,
store: new SQLiteStore({ db: 'sessions.db', dir: './var/db' })
}));
app.use(passport.authenticate('session'));

这应该添加了身份验证所需的所有代码。现在我们可以继续执行它。 

对于Express.js来说,以下路由体现了不受保护的端点,因为它不强制执行任何身份验证:

app.post('/admin', function (req, res) {
// ADMIN AREA
});

此端点对所有人开放,并且必须受到某种身份验证机制的保护。 

值得庆幸的是,Express.js 允许你将身份验证机制实现为中间件。因此,保护​​此端点的直接方法如下:

function authenticate(req, res, next) {
if (!req.session.isLoggedIn) {
res.redirect('/index.html');
} else {
next();
}
}

您也可以直接在路由中插入身份验证,如下所示: 

app.post('/admin', authenticate, function (req, res) { /***/ });

就是这样。

解决破坏的访问控制漏洞

鉴于大多数访问控制缓解措施在所有技术堆栈中都是标准的,让我们简要回顾一下我们在之前关于这个主题的文章中已经为各种漏洞确定的内容。 

不安全的 ID:您可以通过将全局唯一标识符号GUID)实现为 ID 来轻松实现此解决方案。您必须尽早考虑到此漏洞来开发系统。所有 ID(或属于敏感资源的 ID)都必须经过混淆且唯一。 

路径遍历:路径遍历缓解需要验证所有用户输入并限制对关键资源的访问。幸运的是,得益于Spring Security等库的强大功能,您无需做太多工作即可实施正确的路径遍历策略。 

文件权限:除非您需要修改服务器权限并添加与文件操作相关的功能,否则您无需做太多工作来确保文件权限安全。不过,如果您需要进一步的说明,请咨询您的服务器管理员。 

客户端缓存:在这种情况下,最有效的解决方案也是最简单的。不要将敏感的用户信息存储在客户端浏览器中。但是,如果由于需求而必须尝试复杂的功能,请实施适当的 HTML 元标记和异步验证以在页面加载时确认权限。 

结论

随着时间的推移,威胁变得越来越普遍和危险。为了缓解威胁并确保用户的安全,软件公司不断采用更复杂、更强大的解决方案。作为开发人员,我们有责任确保充分利用可用的解决方案,保护我们的平台免受网络上不良行为者的侵害。 

此外,确保我们平台的稳定性和用户信息的安全是一个关键问题,而且随着时间的推移它会变得越来越复杂和敏感。 

文章来源:NodeJS Broken Access Control Guide: Examples and Prevention

#你可能也喜欢这些API文章!