2

I'm creating a web app using Mongoose/MongoDB to store information that will be voted on. I'll be storing usernames and IP addresses with the vote (so voters can update/modify their votes if desired).

Root Question: What's the best way to securely architecture voting in a Mongoose schema?

Currently, my schema looks like this (simplified):

var Thing = new Schema({
  title: {
    type: String
  },
  creator: {
    type: String
  },
  options: [{
    description: {
      type: String
    },
    votes: [{
      username: {
        type: String
      },
      ip: {
        type: String
      }
    }]
  }]
});

mongoose.model('Thing', Thing);

While this makes querying the db for any given Thing super easy, it becomes more problematic for security for obvious reasons - I don't want to be returning out usernames and ip addresses to the browser.

The problem is, I'm not sure which is the best/least painful scenario for securely returning Thing data to the browser:

  1. Loop through each option in Thing.options, then sub-loop through each vote in Thing.options[i].votes to find the vote cast by the user requesting the data, then delete all votes to get rid of other user data. This seems to be very resource intensive, but I couldn't find a way to use indexOf in subarrays (guidance welcome on this one), i.e. Thing.options.votes.indexOf(username) or something to that effect.

  2. Store vote information in the already-existing User schema, then have to search through all users for vote data and stick it all together every time I want query a single Thing. This also seems inefficient/more resource intensive/more complicated than necessary.

  3. Create a separate Vote schema that stores the data more conveniently, but then adds another database call (one for the Thing, one for the Vote).

This problem is somewhat compounded by the fact that there are different ways to vote, with this being the simplest.

Research...for posterity's sake:

This question addresses voting in databases, but for a relational db, not MongoDB/Mongoose.

This question addresses Mongoose/Node.js app architecture, but nothing about votes.

This NPM Module adds voting to Mongoose schemas, but doesn't quite fit my needs.

This post looks very promising, as the author is sort of doing what I'm describing in point 1 above (see Listing 13 on the author's post), but he still creates a nested loop, starting in line 22 of Listing 13, to loop through each choice/option, then through each vote for each choice/option.

Community
  • 1
  • 1
aikorei
  • 570
  • 1
  • 7
  • 23
  • " I don't want to be returning out usernames and ip addresses to the browser" why would you think that? You could perform operations on the IP addresses in your `server.js` and not have to send the actual IP to the browser via `res.send()` for example. – jack blank May 28 '16 at 22:18
  • When I'm sending vote-specific details to a user, the current Schema would send all other user data to them...I don't really want anyone having access to my users' usernames and ip addresses. – aikorei May 29 '16 at 05:36

1 Answers1

1

As a quick hint - to prevent leaking of IP addresses from DB - I would suggest to add extra collection which will store all vote sensitive data, but still have other vote data in same document.

This gives small overhead when storing data, but by design IP info will be not provided to caller and there is no need for extra data scrubbing on every call, to secure data.

profesor79
  • 9,213
  • 3
  • 31
  • 52
  • The more I thought about it, the better this option sounded. It added some query complexity, but ultimately I created a separate Schema for votes, and simply added the vote ID to the `Thing`'s relevant option. It feels like a much cleaner solution than doing nested loops to remove data I don't want to surface to users. Thanks for the second opinion. – aikorei May 30 '16 at 07:52