0

I'm currently working on a simple API that's going to be the backend of my app. I'm designing my data structures right now and have some questions about security best practises.

Project background

The project is written in node.js

Since this is going to be an API (with a couple of protected methods) I think it's best to use an access token based authentication system. (user logs in and retrieves their token, which can then be used to access the protected methods)

The data is structured in such a way that every user has both a unique username and a unique token. For the purpose of this question (and my project) let's assume that this token never ever changes. However it is stored in plain text in the database.

To make sure that people with access to the database don't have direct access to the API by just copying this token I encode the token before I send it out. This way the actual authentication token is not the same as the token stored in the database but the server can decode the auth_token to recreate the token and blah blah..

However people with access to the database can just encode the token themselves... And I'm trying to find out if there's a workaround to this problem.

My question:

I'm trying to find out if there's an even safer way to do this. So please give me all of your ideas and best practise rules etc.

BTW, I considered using a salted hash as encryption but this creates the exact same problem, to match the stored token with the actual access token you'll need to also store the salt, since you can't revert an encryption like that. And storing the salt gives people with access to the database a way to create the actual access token...


PS: I'm trying to create the entire authentication process myself, so please don't suggest things like passport.js. I mean there are great helper tools out there, I just want to do this all manually.

[Update] project background

Down here I'll attempt to sketch some more background to this project. It is indeed a project for high school informatics. (as mentioned below) The idea is simple: a TODO app, you might have heard of node-todo, that's basically it.

This is a group assignment and the levels of expertise among the group members highly varies. (Some have only ever done HTML and (some) CSS, others have done some JS) I've taken the responsibility of creating the backside of the application (the API to interact with the database) since none of the other group members know how. Since the other group members are not all as experienced I have no clue what the front end is going to look like. (My best guess it'll start off a web app)

So basically I want to create a backend that works for every client, and it should be independent. That is, the client is not going to be hosted on the same server (most likely the test are going to be locally only).

API-oriented. Modern applications compose and expose APIs everywhere. They build on open web techniques and use REST, XML and JSON to make it easy for all types of devices and clients to easily consume data. Any visible service or piece of data has a "headless" API counterpart, so that alternative views can be provided. In many cases, the APIs that a modern application exposes form the basis for a public, 3rd party developer community that enables mash-ups, plug-ins and innovation on a core set of data and services that drive a company's business ~ zdnet.com

3 Answers3

1

If you do not mind saving state on your server, you could use a hashtable to save tokens:

Once the user logs in, you generate a random hash. This hash is used as key in the table. You can verify incoming requests by validating that the used token is indeed a key in your table.

To be able to identify which user is logged in, you should add a user identifier (like user email) to the saved object. You might also want to add the used IP-address and an expiration date to this object to further improve security.

By using this technique, you do not need to save tokens in your database :) Only hashed passwords

(You might want to clean your hashtable now and again to delete expired entrees)

Good luck on your project and say hi to Ramon for me

  • So basically you're saying: create random hashes on the fly, and match them to a database ID for each user (or something like that), store this table in a cache and make sure the cache is emptied regularly (every day or so). This way the hash does not contain any info about the the record in the database, and is only found on the server. (The caching is needed for if the server crashes or stops, we don't want to lose sessions) – Jesse van der Pluijm Feb 13 '17 at 09:49
  • Exactly. Because those hashes are random, there is no way for hackers to collect them based on the database and even if they manage to guess a correct hash, the IP addresses will not match. –  Feb 13 '17 at 10:36
  • Do you know of any JS (node/npm) modules that implement this strategy? I could build out the module myself however I have no clue on how to do the IP address part etc. (I might skip that part as it sidetracks me pretty far away from the rest of the assignment. (theres some time pressure here)) – Jesse van der Pluijm Feb 13 '17 at 10:40
  • You don't have to keep sessions saved between server restarts. If you do, there is a possibility for other programs to also load (and possibly dump) this data. The only downside is that if a server restarts while users are actively using the application, they need to create a new token and re-login. On collecting the IP address of a request: http://stackoverflow.com/questions/8107856/how-to-determine-a-users-ip-address-in-node I am not sure about any modules that implement this strategy, as I just thought of this possible implementation. –  Feb 13 '17 at 11:24
  • True, the hash store could just be an `Object` living in memory, and this would do on the long run, I mean you don't expect the server to stop running. However prompting the client to reenter their login is something I don't really want to do (I'm not **that** worried about other programs on the same server collection data) – Jesse van der Pluijm Feb 13 '17 at 12:42
  • I've created a basic library that implements this stratify. I plan on pushing it out to `npm` sometime, however don't feel like it's just there yet. Let's first use it and see just how many problems I encounter. If you're interested: [see the gist](https://gist.github.com/jessevdp/f9d7f638e876833605940c8d855b7092) – Jesse van der Pluijm Feb 13 '17 at 21:05
  • Update: module is now functional: [view on github](https://github.com/jessevdp/tokenstorage) – Jesse van der Pluijm Feb 15 '17 at 06:53
0

I usually just use jwt with it's own salting process. I don't store the token at all in my db, but just use it to store the user's information and set an expiry of 8 hours or so.. This way, you don't need to store tokens in the db, you just decode the token on each request and access the userID stored within its payload. The only downside is if someone steals someone elses token, but you can add in an IP address to the payload and make sure the clients ip is the same as the one stored in the token.

I'm not using passport, but I am using bcrypt for password encrption and JWT for my tokens. Sorry if this isn't what your looking for. Cheers.

Here is my auth library:

(function() {
  var bcrypt = require('bcrypt-nodejs'),
    jwt = require('jsonwebtoken'),
    helpers = require('../lib/helpers.lib.js');

  var JWT_SECRET = 'XXXXX';

  module.exports = function() {
    var self = this;

    //Returns true if valid token given, false otherwise
    self.validToken = function(token) {
      if (token) {
        try {
          var decoded = jwt.verify(token, JWT_SECRET);
          return true;
        } catch (err) {
          // console.log(err)
          return false;
        }
      } else {
        return false;
      }
    };

    self.decodeToken = function(token) {
      if (!token) {
        return null;
      }
      try {
        var decoded = jwt.verify(token, JWT_SECRET);
        return decoded;
      } catch (err) {
        // console.log(err)
        if (err.name == "TokenExpiredError") {
          return 'expired';
        }
        return null;
      }
    };

    self.createToken = function(payload, expires) {
      options = { "expiresIn": ((expires) ? '2d' : '14d') };
      return jwt.sign(payload, JWT_SECRET, options);
    };

    self.hashPassword = function(password) {
      return bcrypt.hashSync(password)
    };

    self.comparePassword = function(plainTextPassword, hashedPassword) {
      return bcrypt.compareSync(plainTextPassword, hashedPassword);
    };

    return self;
  };
}());
matt
  • 1,680
  • 1
  • 13
  • 16
  • So if I'm correct the access token stores a userID? You use JWS to decode your access token and then access the data on the database using this id? – Jesse van der Pluijm Feb 12 '17 at 20:50
  • Yup! `self.createToken = function(payload, expires)` The payload element contains the userID, when I decode it, it returns the payload or an expired error. The userID is then used to access the database. You can store whatever you want in the payload object. Like if the user is an admin etc. – matt Feb 12 '17 at 20:53
  • While that's a bit more fancy than taking the exact token and encrypting it I don't really see how it is different. You basically take something out of the database, encrypt it and use that as token. Or am I missing anything? – Jesse van der Pluijm Feb 12 '17 at 20:58
  • I guess you're right; having a generated token that's stored in the database is the same thing.. Off hand I can't see any benefit to using a userID vs a static token other then a token is an extra field in the database you don't need. How are you sending your tokens on your requests? Are you sending them as a Authorization Header or as a param? – matt Feb 12 '17 at 21:03
  • For the token I planned to use the database ID (encrypted) to save myself some work on creating a whole new (random yet unique) token. I would have to encrypt it to make sure people can't edit the record directly using the database but be forced to use the API... I'm planning on just sending the token trough a param. I chose it to be static to save myself the hassle of implementing a TTL etc. Since I want to do this manually. – Jesse van der Pluijm Feb 12 '17 at 21:10
  • Sounds good to me. Lots of APIs use the method you are suggesting. Just make sure whatever database your using is secured on the server. If you're using mongo see [here](https://docs.mongodb.com/manual/administration/security-checklist/) . – matt Feb 12 '17 at 21:15
  • Alrighty, thank you for your input. I'm going to leave this question open for now as I'm curious what others have to say. – Jesse van der Pluijm Feb 12 '17 at 21:23
  • Aye, always good to get lots of opinions, I'm also interested in how other people do their authentication as there could be something key im missing. Cheers. – matt Feb 12 '17 at 21:35
0

multiple things come in mind: - have you looked at Firebase? - what about things like two factor authentication (e.g. speakeasy or authy)?

You do not give too many details on the project background, I understood from mr. Moorlag that it has to do with high school informatics. So in case you want to make a proof-of-concept (e.g. for a Profielwerkstuk (± high school thesis in Dutch schools)) you would have different requirements than 'just implementing an app' that you would want to publish in the Play / App Store of course.

Some minor students of mine did a workshop two weeks ago about Firebase and then you do not need to worry about such a things, works really great (note: Firebase is NoSQL so forget normalization ;)). If you want to do research on it I would find out the details about two factor authentication and also have a look at TLS / HTTPS (Let's Encrypt?! yay!).

JopieK
  • 68
  • 9
  • Heya, I updated the project background a little bit, see the updated question above. (I've taken a quick look at firebase, and from a quick overview it seems to be developing applications without having to create the backend? am I correct or is this horribly wrong?) (Let's Encrypt is a **must** for production yes! ;) hehe ) – Jesse van der Pluijm Feb 13 '17 at 09:45
  • Wikipedia > Firebase provides a realtime database and backend as a service. The service provides application developers an API that allows application data to be synchronized across clients and stored on Firebase's cloud. The company provides client libraries that enable integration with Android, iOS, JavaScript, Java, Objective-C, swift and Node.js applications. The database is also accessible through a REST API and bindings for several JavaScript frameworks such as AngularJS, React, Ember.js and Backbone.js. – Jesse van der Pluijm Feb 13 '17 at 09:56
  • Firebase ís the backend so in essence you are correct, sorry for not answering sooner! – JopieK Feb 27 '17 at 18:44