NodeJS:用户可以通过导航目录绕过路由处理程序

问题描述 投票:0回答:2

我是 Node.js 新手,我创建了一个登录系统。我的应用程序位于域的子目录中,例如:https://domain.tld/nrjs/。该应用程序中有两个包含静态文件的文件夹。一个文件夹称为

public
,另一个文件夹称为
protected
。当用户通过 https://domain.tld/nrjs/ 连接到节点应用程序时,他们将获得静态文件夹
public
并定向到
login.html
页面。一旦通过身份验证,它们就会被提供给静态文件夹
protected
,并发送到 https://domain.tld/nrjs/index.html,该文件夹存储在
protected
文件夹中。问题是,似乎 app.js 是否向他们发送文件夹(无论他们是否有身份验证),用户都可以直接链接到位于 https://domain.tld/nrjs/protected/index 的文件夹。 html 或任何其他实际文件,它完全忽略我的 app.js 文件中的任何路由处理程序。我正在使用 cPanel。下面是我的 app.js,但似乎用户可以直接链接到该文件,而 app.js 甚至不涉及。我需要能够阻止用户访问
protected
文件夹,直到他们成功登录,或者完全阻止他们访问它也可以,因为我将在
/index.html
而不是
/protected/index.html 向他们发送文件
.

结构:

public_html/
│
└───nrjs/                 (node.js app is in subdirectory) 
    │   app.js
    │
    ├───public/
    │       login.html    (Publicly accessible login page)
    │
    └───protected/
            index.html    (Private home page, should not be directly accessible)

这是我的app.js:

const express = require('express');
const fs = require('fs');
const path = require('path');
const session = require('express-session');
const bodyParser = require('body-parser');
const bcrypt = require('bcryptjs');

const app = express();
const basePath = '/nrjs';
const saltRounds = 10;

let users = [
    { username: 'admin', password: 'password' },
    { username: 'user1', password: 'password1' },
    { username: 'user2', password: 'password2' }
];

Promise.all(users.map(user =>
    bcrypt.hash(user.password, saltRounds).then(hash => {
        user.password = hash;
    })
)).then(() => {
    console.log('Users initialized with hashed passwords');
});

app.use(session({
    secret: 'KAL783BB4HJK1P90V3',
    resave: false,
    saveUninitialized: true
}));

app.use(bodyParser.urlencoded({ extended: true }));

app.use(basePath, express.static(path.join(__dirname, 'public')));

function checkAuthenticated(req, res, next) {
    if (!req.session.user && req.path !== '/login.html' && req.path !== '/login') {
        return res.redirect(basePath + '/login.html');
    }
    next(); 
}

app.use(basePath, checkAuthenticated, express.static(path.join(__dirname, 'protected')));

app.post(basePath + '/login', (req, res) => {
    const { username, password } = req.body;
    const user = users.find(u => u.username === username);

    if (user) {
        bcrypt.compare(password, user.password, (err, isMatch) => {
            if (err) {
                console.error('Error during password comparison:', err);
                return res.redirect(basePath + '/login.html?error=An error occurred');
            }

            if (isMatch) {
                req.session.user = user.username;

                const loginLog = `Successful login by ${user.username} at ${new Date().toLocaleString()}\n`;
                fs.appendFile('logs/success_login_attempts.log', loginLog, (err) => {
                    if (err) {
                        console.error('Error writing to login log file:', err);
                    }
                });

                res.redirect(basePath + '/index.html');
            } else {
                const failedLoginLog = `Failed login attempt with username: ${username} at ${new Date().toLocaleString()}\n`;
                fs.appendFile('logs/failed_login_attempts.log', failedLoginLog, (err) => {
                    if (err) {
                        console.error('Error writing to failed login log file:', err);
                    }
                });

                res.redirect(basePath + '/login.html?error=Incorrect username or password');
            }
        });
    } else {
        const failedLoginLog = `Failed login attempt with username: ${username} at ${new Date().toLocaleString()}\n`;
        fs.appendFile('logs/failed_login_attempts.log', failedLoginLog, (err) => {
            if (err) {
                console.error('Error writing to failed login log file:', err);
            }
        });

        res.redirect(basePath + '/login.html?error=Incorrect username or password');
    }
});

app.get(basePath + '/login', (req, res) => {
    res.redirect(basePath + '/login.html');
});

app.get(basePath + '/logout', (req, res) => {
    req.session.destroy();
    res.redirect(basePath + '/login.html');
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

我尝试将各种路由处理程序放在页面顶部,并做了一个包罗万象的路由处理程序,它仍然让用户直接导航到文件。

示例:

app.get('/nrjs/protected', (req, res) => {
    res.status(403).send('Access Denied');
});

我还尝试使用路线通配符:

app.get('/nrjs/protected/*', (req, res) => {
    res.status(403).send('Access Denied');
});

并且当尝试捕获所有内容时,他们仍然可以导航到受保护的文件:

app.use((req, res, next) => {
    res.status(404).send("Sorry, the page you're looking for doesn't exist.");
});

我还尝试使用

protected
阻止对
.htaccess
文件夹的访问,并在
protected
文件夹之外的路由上收到 503 错误。

编辑:我已将节点应用程序移至 Web 目录之外,因此用户无法再直接导航到应用程序静态文件,从而解决了问题。但是,我仍然想知道如果我确实将节点应用程序存储在 Web 目录中,是否有办法阻止用户导航文件。当用户成功导航到受保护目录时,我可以确认没有设置会话。谢谢!

javascript node.js routes directory cpanel
2个回答
0
投票

您也有运行网络服务器吗?

如果是这样,那么您需要将其配置为不共享文件夹。对于 Apache,这是通过添加 .htaccess 文件来完成的。

这个answer解释了如何配置它。


0
投票

可以在浏览器中访问

.jpg
.css
目录中的所有可通过 HTTP 访问的文件,例如
.js
.txt
.html
public
protected
)只需在 url 中导航到它们即可。这是因为无论从
public_html/
目录(可能是 Apache)提供文件,都是提供
protected/index.html
而不是 Express。这是一个插图。

这是您的域名:

https://domain.tld

用户向此文件发出

GET
请求:

https://domain.tld/pancakes.html

Apache(为了论证)从这里提供文件:

/var/www/html/public_html/pancakes.html

用户然后向此路由发出

GET
请求,例如:

https://domain.tld/nrjs/login

那么,您必须有一些东西侦听端口 443 上到

https://domain.tld/nrjs
的流量,并将其路由到端口 3000,以便 Express 可以接收它。

当 Express 在端口 3000 上收到该请求时,它会向

redirect
发送
nrjs/login.html
。那么,您正在使用
app.use(basePath, express.static(path.join(__dirname, 'public')));
中间件,因此 Express 会在
login.html
文件夹中看到
public
,然后将 html 发送回用户。

接下来,用户向此处发送

POST
请求:

https://domain.tld/nrjs/login

再次重新路由到端口 3000,并经过几次密码验证等后将

redirects
表示为
nrjs/index.html
。它通过
static
中间件查看
public
目录,找不到它。然后它通过
static
中间件查看
protected
目录和宾果游戏,它提供了
index.html

现在您拥有的是一个

checkAuthenticated
中间件,确保它是正在寻找
index.html
的经过身份验证的用户,但这仅适用于在端口 3000 上向
https://domain.tld/nrjs/index.html
发出的请求。

如果用户导航至:

https://domain.tld/nrjs/protected/index.html

然后看起来 Apache 只是在端口 443 上接收并提供服务。 Express 没有任何

protected/index.html
的路线处理程序,它只有
nrjs/index.html

我的建议是:

  1. 一起删除
    protected
    文件夹。
  2. index.html
    移动到与
    app.js
    相同的目录。
  3. 更改此:
res.redirect(basePath + '/index.html');

到此

res.sendFile(__dirname + '/index.html')
  1. 删除此:
app.use(basePath, checkAuthenticated, express.static(path.join(__dirname, 'protected')));
  1. 添加此:
app.get(basePath + '/index', checkAuthenticated, (req, res) => {
    res.sendFile(__dirname + '/index.html')
});

现在,用户访问

index.html
的唯一方法是通过登录过程或通过
https://domain.tld/nrjs/login
路由,他们需要首先通过
checkAuthenticated
中间件。

© www.soinside.com 2019 - 2024. All rights reserved.