使投票系统安全 Meteor.Methods

Making Vote System Secure Meteor.Methods

我正在尝试实施一个安全的投票系统,并且无法通过客户端控制台的 Meteor.call() 进行更改。每个人都可以在登录后对 post 进行上下投票。但每个用户和 post 一次只能投票一次。

只有我的客户端我得到了这样的东西:

Template.postArgument.events({
 'click .yes':function() {
       if(Meteor.user()) {
        var postId = Arguments.findOne({_id:this._id})
        var currentArgumentId = this._id;
        if($.inArray(Meteor.userId(), postId.votedUp) ===-1) {
          if($.inArray(Meteor.userId(), postId.votedDown) !==-1) {
            Meteor.call('argumentVoteYes',currentArgumentId);
          } else {
            Meteor.call('argumentVoteYesElse',currentArgumentId);
          } 
        } else {
          Meteor.call('argumentVoteYesElseElse',currentArgumentId);
        }
      }
    }}
)};

在我的服务器上:

    Meteor.methods({
    'argumentVoteYes':function(currentArgumentId){
        Arguments.update(currentArgumentId, {
            $pull: {votedDown: Meteor.userId()},
            $inc: {score: 2 },
                $addToSet: {votedUp: Meteor.userId() }
              });
      },
      'argumentVoteYesElse':function(currentArgumentId){
        Arguments.update(currentArgumentId, {
            $inc: {score: 1 },
            $addToSet: {votedUp: Meteor.userId() }
              });
      },
      'argumentVoteYesElseElse':function(currentArgumentId){
        Arguments.update(currentArgumentId, {
            $inc: {score: -1 },
            $pull: {votedUp: Meteor.userId()}
            });
      }
    'argumentVoteNo':function(currentArgumentId){
    Arguments.update(currentArgumentId, {
        $pull: {votedUp: Meteor.userId()},
        $inc: {score: -2 },
        $addToSet: {votedDown: Meteor.userId() },
        });
  },
  'argumentVoteNoElse':function(currentArgumentId){
    Arguments.update(currentArgumentId, {
        $inc: {score: -1 },
        $addToSet: {votedDown: Meteor.userId() },
        });

  },
  'argumentVoteNoElseElse':function(currentArgumentId){
    Arguments.update(currentArgumentId, {
        $inc: {score: 1 },
        $pull: {votedDown: Meteor.userId()}
        }); 
  },
    });

问题是我如何确保这个安全,例如,如果有人调用 Meteor.call('argumentvoteYes', "someID" , {$inc: {score:2}}); 它将增加 2 的分数。如果用户调用它两次,投票将增加 4。有什么办法吗这样做安全吗?

您需要检查用户是否在服务器上的 votedUp/down 数组中。您在客户端拥有正确的逻辑,因此只需在服务器上进行实际更新之前应用相同的逻辑即可。

您不必担心方法中会出现额外的增量,因为您的方法只接受一个参数。但是,您确实需要防范其他黑客攻击:

让我们首先扩展 Match 对象,以便我们可以检查 _id 是否就是:

Match._id = Match.Where(function(id){
  check(id, String); // first make sure we're dealing with a string
  // then grep for an exactly 17 character alphanumeric string
  return /^[a-zA-Z0-9]{17,17}/.test(id); 
});

more on this technique

现在让我们来看看你的第一个方法:

Meteor.methods({
  'argumentVoteYes':function(currentArgumentId){
    check(currentArgumentId,Match._id); // will error if not an _id

    var post = Arguments.findOne({ _id: currentArgumentId });
    var userId = Meteor.userId(); // the current user

    if ( post && userId ){ // make sure a real user is operating on an actual document
      // only update if no votes have been recorded OR
      // user hasn't already voted

      if ( !post.votedUp || post.votedUp.indexOf(userId) === -1 ){ 
        Arguments.update(currentArgumentId, {
          $pull: { votedDown: userId },
          $inc: { score: 2 },
          $addToSet: {votedUp: userId }
        });
      }
    }
  },

我之前也有同样的想法,我找到了解决方案,只使用一个你不从客户端传递的 userId 数组(你只保存当前用户的 ID)并计算数组中的总 ID。它不会添加相同的 ID 两次。

从我的代码(你可以做剩下的检查,比如用户是否在不喜欢的数组中,做一些事情等):

likeActivity: function (activityId) {
    Activities.update(activityId,{
        $addToSet: {
            likers: this.userId,
        }
    });
},

unlikeActivity: function (activityId) {
    Activities.update(activityId,{
        $pull: {
            likers: this.userId,
        }
    })
}

在我的助手中:

likeCount: function() {
    return this.likers.length
}