MongoDB中的NoSQL注入:漏洞机制和防范措施
在使用数据库的系统安全性方面,需要特别注意的一项是SQL注入。SQL注入是一种通过外部意外输入而执行恶意SQL语句的漏洞。当在操作SQL数据库时,如果实现中将用户输入不正确地验证和转义并嵌入到SQL查询中,则会发生SQL注入。
使用NoSQL数据库可以避免SQL注入的发生,但同时要注意NoSQL注入的问题。
本文将以使用MongoDB实现系统的示例为基础,解释NoSQL注入。
NoSQL与SQL的区别。SQL是Structured Query Language的简称,是用于操作和获取关系型数据库数据的标准编程语言。
NoSQL是指采用与关系型数据库不同的数据模型的数据库的统称。像MongoDB和Redis这样的数据库属于NoSQL分类。
SQL 是一种编程语言,而 NoSQL 是指不使用 SQL 的非关系数据库。
什么是NoSQL注入攻击?NoSQL注入是指在NoSQL数据库中发生的注入漏洞。在NoSQL中,不使用SQL语句来操作数据,而是使用自定义的查询语言和API。由于不同类型的数据库有不同的操作方法,注入的发生模式也不是统一的。
然而,还有一些共同的措施可以采取。
-
- パラメータ化されたクエリを使用する
- リクエストパラメータが不正な形式だった場合は処理を行わない
通过进行诸如此类的行动,可以降低风险。
脆弱实现的示例
使用环境
-
プログラミング言語
-
- プログラミング言語
Node.js
データベース
MongoDB
ライブラリ
mongoose
express
我们将以一个简单的Web服务登录用API为例来进行解释。数据操作将使用MongoDB操作库“mongoose”。
用户模型
username: String
password: String
终端点:POST /登录
发送JSON数据
{
username: 'john',
password: 'hoge',
}
案例1:评估表达式的改写
app.post('/login', async (req, res) => {
const username = req.body.username;
const password = req.body.password;
const users = await User.find({
$where: `this.username === '${username}' && this.password === '${password}'`
});
if (users.length === 0) {
res.json({
message: 'Failed to log in.'
});
return;
}
res.json({
message: `Successfully logged in as ${users[0].username}`
});
});
$where: `this.username === '${username}' && this.password === '${password}'`
username: String
password: String
发送JSON数据
{
username: 'john',
password: 'hoge',
}
案例1:评估表达式的改写
app.post('/login', async (req, res) => {
const username = req.body.username;
const password = req.body.password;
const users = await User.find({
$where: `this.username === '${username}' && this.password === '${password}'`
});
if (users.length === 0) {
res.json({
message: 'Failed to log in.'
});
return;
}
res.json({
message: `Successfully logged in as ${users[0].username}`
});
});
$where: `this.username === '${username}' && this.password === '${password}'`
{
username: 'john',
password: 'hoge',
}
app.post('/login', async (req, res) => {
const username = req.body.username;
const password = req.body.password;
const users = await User.find({
$where: `this.username === '${username}' && this.password === '${password}'`
});
if (users.length === 0) {
res.json({
message: 'Failed to log in.'
});
return;
}
res.json({
message: `Successfully logged in as ${users[0].username}`
});
});
$where: `this.username === '${username}' && this.password === '${password}'`
在这种实现情况下,通常会按照以下方式执行。
$where: `this.username === 'john' && this.password === 'hoge'`
如果用户名或密码中包含单引号字符串,可以添加新的条件语句。
$where: `this.username === 'john' && this.password === '' || this.username === 'admin'`
当您插入这样的字符串时,即可无需密码以其他用户身份登录。
作为应对措施,可以停止使用$where,并将其替换为以下参数化查询。
const users = await User.find({
username: username,
password: password,
})
情景2:增加操作员
app.post('/login', async (req, res) => {
const username = req.body.username;
const password = req.body.password;
const users = await User.find({
username: username,
password: password,
});
if (users.length === 0) {
res.json({
message: 'Failed to log in.'
});
return;
}
res.json({
message: `Successfully logged in as ${users[0].username}`
});
});
app.post('/login', async (req, res) => {
const username = req.body.username;
const password = req.body.password;
const users = await User.find({
username: username,
password: password,
});
if (users.length === 0) {
res.json({
message: 'Failed to log in.'
});
return;
}
res.json({
message: `Successfully logged in as ${users[0].username}`
});
});
如果实施如此,通常会执行以下操作。
const users = await User.find({
username: 'john',
password: 'hoge',
});
如果将这个用户名和密码以对象形式而不是字符串形式接收的话,
const users = await User.find({
username: 'john',
password: {
$ne: 'dummy'
},
});
$ne是用于MongoDB的比较运算符。它表示”不等于”,因此即使密码不匹配,登录也会成功。
对策方面,以下的修改方法是有效的。
-
- パラメータの型がオブジェクト型ではないことを確認する
- パラメータの型が適切な型である(例では文字列型)ことを確認する
总结在使用像MongoDB等NoSQL数据库的情况下,我们需要找出可能发生注入的查询语言或API的地方,并进行适当的输入检查。
在MongoDB的情况下,您可以通过以下方法来防止:
-
- パラメータ化されたクエリの使用($whereを使用しない)
- パラメータの型のバリデーション
最後AeyeScan是一种SaaS型的Web应用程序漏洞扫描平台。由于可以快速准确地进行Web扫描,因此被许多企业用作Web应用程序扫描的内部工具。
如果对我们在本次解释中介绍的MongoDB的NoSQL注入感兴趣的话,您可以通过试用来测试。有关AeyeScan的详细信息,请访问:https://www.aeyescan.jp