Scenario:
Some time ago, a new project was presented to me. I had to design the solution.
Among other stuff, that I will talk later, one of the challenges was about communicate a set of services that were expose to the public internet. Despite I was not comfortable about that, it was one of the requirements; a custom app had to be able to talk to the API directly, while most of the apps will talk to a specific API (entry point, EP). This EP then will have to talk to other services as well.
Then, the problem was: How do I control that every request between two APIs were actually allowed to ask and read information?
Every auth I had used at that time were in the form of: 1. I were provided a pair of key values to send in headers every time. 2. I had to ask for a new token every time I want to send a message.
But this time I wanted to make something different, and put in practise an advice given to me by an older college: triangles tends to be safe.
The third guy:
To make my triangle, I created a new API that was going to be the source of true for permissions. Lets name it SoT.
Then, the general algorithm was:
- Every API will have its own secret key.
- SoT will have everybody’s secrets key.
- When a request comes from EP, a transaction will be created on SoT.
- Every request will be signed by emitter with secret and transaction.
- SoT will be the only capable of tell if a signature is right or not.
- Every time an API receives a requests, will ask to SoT if request’s signature is valid.
Implementation:
Actually, coding that solution was quite straight forward; I needed two clients components and the SoT controllers:
From EP ask for create and sign a transaction
transactionId = new uuid()
sign = new Hash(EP_SECRET_KEY, transactionId)
// EntryPoint as requester API name for signer field
ok = sot.create(‘EntryPoint’, transactionId, sign)
Into SoT
remoteKey = keys.get(input.signer)
if input.sign == new Has(remoteKey, input.transactionId)
createTransaction()
else
Error(403, ‘Can not create transaction’)
From a requested API (as endpoint middleware)
if !sot.validate(input.sign, input.transactionId, input.signer)
Error(403, ‘Invalid signature’)
continue()
Once a request was authenticated, other internal calls can be authed in the same way.
About the secrets, they should be generated randomly in deploy time. Every time we deploy an API, we renew its secret and let SoT know about the change. They are stored in env vars.
Risks:
Not really found a lot to concern about. As we keep secrets real secrets, we should have no concern. Who want to make unauthorized requests should know the secrets and the hash function implemented.
Sum up:
It’s quite easy to implement triangle authentications by our own with certain security level. What we need to care about is keep keys secrets and don’t expose our hash functions.
Gretings:
Thanks for the read. Any suggestions or comments are appreciated.
Top comments (0)