我在我的meteor应用程序中发现了一个关于订阅的重要安全错误(也许方法也受此影响)。
即使我使用check
包和check()
确保在发布内部接收到正确的参数数据类型,我也意识到如果用户恶意订阅具有错误参数数据类型的订阅它影响使用相同订阅的所有其他用户,因为当恶意用户使用不正确的参数时,流星服务器未运行发布。
我该如何防止这种情况?
使用的套餐:
aldeed:collection2-core@2.0.1
audit-argument-checks@1.0.7
mdg:validated-method
和npm
import { check, Match } from 'meteor/check';
服务器端:
Meteor.publish('postersPub', function postersPub(params) {
check(params, {
size: String,
section: String,
});
return Posters.find({
section: params.section,
size: params.size === 'large' ? 'large' : 'small',
}, {
// fields: { ... }
// sort: { ... }
});
});
客户方:
// in the template:
Meteor.subscribe('postersPub', { size: 'large', section: 'movies' });
// Malicious user in the browser console:
Meteor.subscribe('postersPub', { size: undefined, section: '' });
问题:恶意用户订阅阻止所有其他用户从postersPub
订阅中获得答案。
额外注意:我还尝试用try catch
包装检查块和整个出版物,但它不会改变效果。该错误从服务器控制台消失,但其他用户不断受到影响,并且没有从恶意用户正在影响的订阅中获取数据。
答案 0 :(得分:5)
有一件事需要了解check
和字符串,它接受像''
这样的空字符串,这些字符串基本上是在您的恶意示例中显示的。
如果不保证解决您的出版物问题,我至少可以建议您修改check
代码并包含对非空字符串的检查。
可能的方法是:
import { check, Match } from 'meteor/check';
const nonEmptyString = Match.Where(str => typeof str === 'string' && str.length > 0);
然后可以在check
中使用,如此:
check(params, {
size: nonEmptyString,
section: nonEmptyString,
});
您可能对接受的参数更严格,并将它们减少为有效条目的子集。例如:
const sizes = ['large', 'small'];
const nonEmptyString = str => typeof str === 'string' && str.length > 0;
const validSize = str => nonEmptyString(str) && sizes.indexOf( str) > -1;
check(params, {
size: Match.Where(validSize),
section: Match.Where(nonEmptyString),
});
请注意,这也可以帮助您避免基于参数的查询逻辑。您可以更改以下代码
const posters = Posters.find({
section: params.section,
size: params.size === 'large' ? 'large' : 'small',
}, {
// fields: { ... }
// sort: { ... }
});
到
const posters = Posters.find({
section: params.section,
size: params.size,
}, {
// fields: { ... }
// sort: { ... }
});
因为该方法无论如何只接受large
或small
中的一个作为参数。
可以支持您防止发布错误的另一种模式是,如果集合没有返回游标,则调用this.ready()
(无论出于何种原因,最好是编写好的测试以防止您出现这些情况)。
const posters = Posters.find({
section: params.section,
size: params.size === 'large' ? 'large' : 'small',
}, {
// fields: { ... }
// sort: { ... }
});
// if we have a cursor with count
if (posters && posters.count && posters.count() >= 0)
return posters;
// else signal the subscription
// that we are ready
this.ready();
应用上述所有模式会使您的函数看起来像这样:
import { check, Match } from 'meteor/check';
const sizes = ['large', 'small'];
const nonEmptyString = str => typeof str === 'string' && str.length > 0;
const validSize = str => nonEmptyString(str) && sizes.indexOf( str) > -1;
Meteor.publish('postersPub', function postersPub(params) {
check(params, {
size: Match.Where(validSize),
section: Match.Where(nonEmptyString),
});
const posters = Posters.find({
section: params.section,
size: params.size,
}, {
// fields: { ... }
// sort: { ... }
});
// if we have a cursor with count
if (posters && posters.count && posters.count() >= 0)
return posters;
// else signal the subscription
// that we are ready
this.ready();
});
我自己发现,通过良好的check
匹配和this.ready()
,我的应用程序中的出版物问题已减少到最低限度。