Sep 22, 2017

Securing requests between two back-end applications

I’m working on a super-top-secret-but-not-really-maybe-cryptocurrency-related web product and I eventually ended up needing two back-end applications; one built in Rails and the other in Node.js

The Rails application makes HTTPS requests to the Node.js application for very important things and as such securing that communication is of utmost priority. So I did the usual no-brainer things, such as:

  • Using a very hard to guess password for Heroku and storing it nowhere else besides my noggin. It might seem like something to not worry about, but to me it’s the most important step, because no matter what other security measures you put in place, once someone can control your Heroku (or any service you use for deployment) the jig is up.
  • Using HTTPS for everything. Please. There’s no reason not to, even if you’re not communicating sensitive information. Here’s a good article about Why HTTPS Matters.
  • Confirming the request origin. It’s important to check whether your request is coming from where you expect it to.

Another thing I did was authenticate all requests with a token (in the headers) that was only known to the two applications via environment variables. However, Tal Ater asked me “What happens if a bad actor gets a hold of this token? Wouldn’t that mean they can make an unlimited amount of requests to your very important Node.js application?”

Admittedly, it’s very hard to do, but it is possible for this bad actor to get past HTTPS and obtain this token and I found that it’s relatively trivial to spoof the request origin.

The chances of this happening are very slim, but bad actors need only one win and it’s paramount that my application doesn’t give that to them.

I came up with a couple of requirements:

  • Tokens should not be reusable after x amount of time.
  • Only these two applications are capable of creating/reading these tokens.

And this solution below seems to have it all sorted out:

  1. Encrypt the current time (in whatever format you prefer) with the token as the secret. I used AES 265 encryption with an Initialisation Vector, so that if per chance the same timestamp is being encrypted at the same exact time, the encrypted string won’t be the same. Here’s a good explainer of what an IV is.
  2. Put this encrypted timestamp in the header of all requests, in my case; any request the Rails application makes to the Node.js application.
  3. On the receiving end of the request, decrypt this timestamp with the token safely ensconced in an environment variable. If this decryption fails, decline the request with a polite 401.
  4. Check the database if the timestamp has been used before. If it exists in the database, then reject the request with a cool 401. This prevents a bad actor from trying to reuse a timestamp within the x seconds grace period.
  5. If the decrypted timestamp isn’t between now and x seconds ago, decline the request with a polite 401. As a result of this, every x seconds, that timestamp is no longer valid.
  6. Save the timestamp that was just used to the database, so that it can’t be reused.

I think this manages to solve the problem I had, however, the main reason I’m writing this is to get as many opinions as possible.

Are there any flaws I haven’t seen? What other measures can I put in place?

Let me know if you have any suggestions! Thanks!

Copied to clipboard!