我对Meteor相当新,我只想弄清楚流星的安全性。
我正在编写一个测验应用,允许登录用户保存他们的分数。我创建了一个由用户ID和分数数组组成的集合。我公开推新分数的方式是服务器端的一种方法:
Meteor.methods({
'pushScore' : function(playerId, playerScore) {
UserScores.upsert({ userId : playerId}, {$push : {scores : playerScore}});
}
});
我从客户端点击按钮调用该方法,如下所示:
if (Meteor.userId()){
Meteor.call('pushScore', Meteor.userId(), Session.get("score"));
}
我在这里有以下问题:
这只是一个示例应用程序,但我可以轻松想象一个可以模仿这个的真实场景。在这种情况下,最好的做法是什么?
提前致谢。
干杯..
答案 0 :(得分:4)
正如@Peppe建议的那样,你应该以某种方式将逻辑移到服务器上。 Meteor安全(以及一般的Web安全)的主要规则是
原因就是你已经提到的:如果客户端可以做什么,那么就没有办法阻止流氓用户从浏览器控制台做同样的事情,甚至写自己的恶意客户端将利用泄漏。
在您的情况下,这意味着如果客户端能够将分数添加到分数,那么无论您采用何种安全措施,用户都可以这样做。你可以或多或少地做到这一点,但你的系统有一个设计的泄漏,不能完全关闭。
因此,唯一的防弹解决方案是让服务器决定何时分配点数。我假设在测验应用中,用户在选择问题的正确答案时获得积分。因此,如果答案是正确的,那么不要在客户端上检查,而是创建一个服务器端方法,该方法将接收问题ID,答案ID并增加用户分数。然后确保用户不能仅使用所有可能的答案调用此方法,其方式与您的测验设计相对应 - 例如,如果选择了错误答案则给出负点,或者允许在一段时间内仅回答一次相同的问题。
最后,确保客户端不仅在收到的数据中获得正确的答案ID。
答案 1 :(得分:2)
简而言之,您的问题有两种常见的解决方法:
如果你使用Meteor.method不传递Meteor.call中的任何参数,服务器可以而且应该收集它计划在服务器端插入/更新的数据。
您可以使用集合“allow”方法向集合中添加验证函数,以验证来自客户端的任何更新,在这种情况下,您不需要Meteor.method,只需从客户端进行更新即可验证服务器端。
答案 2 :(得分:2)
meteor中的安全性(插入/更新/删除操作)与任何其他框架中的安全性相同:在执行用户采取的操作之前,请确保用户有权执行该操作。安全性可能在Meteor中显得有些弱点,但它不会受到其它框架的影响(但是,通过控制台在Meteor中更容易利用它)。
解决问题的最佳方式可能因情况而异,但这里有一个例子:如果用户发帖,用户应获得5分。这是一个解决问题的坏方法:
if(Meteor.isClient){
// Insert the post and increase points.
Posts.insert({userId: Meteor.userId(), post: "The post."})
Meteor.users.update(Meteor.userId(), {$inc: {'profile.points': 5}})
}
if(Meteor.isServer){
Posts.allow({
insert: function(userId, doc){
check(doc, {
_id: String,
userId: String,
post: String
})
// You must be yourself.
if(doc.userId != userId){
return false
}
return true
}
})
Meteor.users.allow({
update: function(userId, doc, fieldNames, modifier){
check(modifier, {
$inc: {
'profile.points': Number
}
})
if(modifier.$inc['profile.points'] != 5){
return false
}
return true
}
})
}
是什么让它变坏?用户可以在不发布帖子的情况下增加积分。这是一个更好的解决方案:
if(Meteor.isClient){
// Insert the post and increase points.
Method.call('postAndIncrease', {userId: Meteor.userId(), post: "The post."})
}
if(Meteor.isServer){
Meteor.methods({
postAndIncrease: function(post){
check(post, {
userId: String,
post: String
})
// You must be yourself.
if(post.userId != this.userId){
return false
}
Posts.insert(post)
Meteor.users.update(this.userId, {$inc: {'profile.points': 5}})
}
})
}
更好,但仍然很糟糕。为什么?由于延迟(帖子是在服务器上创建的,而不是客户端)。这是一个更好的解决方案:
if(Meteor.isClient){
// Insert the post and increase points.
Posts.insert({userId: Meteor.userId(), post: "The post."})
}
if(Meteor.isServer){
Posts.allow({
insert: function(userId, doc){
check(doc, {
_id: String,
userId: String,
post: String
})
// You must be yourself.
if(doc.userId != userId){
return false
}
return true
}
})
Posts.find().observe({
added: function(post){
// When new posts are added, the user gain the points.
Meteor.users.update(post.userId, {$inc: {'profile.points': 5}})
}
})
}
这个解决方案唯一的缺点是点数增加的延迟,但这是我们必须忍受的事情(至少在目前)。在服务器上使用observe也可能是一个缺点,但我认为你可以通过使用包集合钩子来传递它。