In security there are no silver bullets. It’s a cliche that rings true, more so now than ever before.
In this blog post, I want to provide some tips for writing secure code in the era of microservcies, and give you a simple metaphor to remember along the way.
First, a little background. I began my career writing “monolithic” server applications, mostly through frameworks such as Django and Plone. The approach was fairly straightforward: all parts of the app had access to the backend database; limits were enforced on the end user.
Now we have moved rapidly to microservices (which I like very much, by the way). Microservices allow micro-releases, moving the development cycle from a quarterly (or longer) “all hands on deck” release to “as soon as it’s ready” release of a minimal feature set, code fix, or both. For this to be successful most of the services need to maintain a level of compatibility in their edges and expected results and deployment steps need to have some level of idempotency (and of course ability to rollback).
The changes in the way we develop software require a change in the mindset of the way we think about and implement security. Security must now be a core component of each service, and security must withstand the changes in other services that interact. Each service must be a secure unit, resilient and able to interact with clients/providers of other functionalities without compromising security. In addition, each service must be careful how its edges present data to ensure every possible interaction can be secured if both ends meet the required security standards, without having to previously coordinate and with the ability to deny service to those who don’t.
Given the security challenges presented by microservices, as a developer I need to change my mindset on how approach privacy and security in my code. I must think about security from the genesis of the program, from within the code itself, instead of relying on the infrastructure to secure the application. Now I start my architectural decision-making by thinking of security as the foundation of my code, instead of an accessory.
The best analogy I can think for my new approach to software development in the age of microservices is that each microservice is a spy or intelligence agent in modern parlance. (Confession: I may have been watching too much of The Man from U.N.C.L.E while ruminating on this.) Much like a spy, each microservice has a precise mission, and works on a need to know basis. Moreover, each service is disposable and can be disowned or torn down, if necessary, for the greater good of the system.
Following my spy conceit — with full awareness and appreciation that there is no single approach, and that security and privacy considerations must be addressed on a case-by-case basis — here are a few guidelines that I find useful for writing software for microservices:
- Compartmentalize missions: Try to make your microservices as essential as possible. Without getting to extremes where you create a swarm of nanoservices doing one task, responsibilities can be reduced to the point where each single process lacks enough information to be a liability if it were to leak it.
- Send Mission Impossible-style messages: I have always been a fan on how Etan Hunt (or Jim Phelps if you are my age) got his messages. Encrypt all the data your microservice needs and send it on a perishable channel. This allows you to cancel the communication channel, rotate the encryption keys used and retry the message if you were to suspend sending information to a node. A good example of this is a node you lost control of in a HA layout. By cancelling all message queues and re-sending the messages with rotated key allows you to continue functioning without worrying that the unresponsive node could be compromised and save you from needing a full stop.
- Use customized signage and codewords: Try to send disposable channel signing and encrypting data to your services that you hold authority over. This will allow you to cancel credentials even without control on the node, and will allow your clients to shun the particular node without having to access to it by just controlling the emitting authority, your node might be compromised but it won’t spread.
- Use as many different keys as you need: Storage is a weak point in terms of encryption. If you rotate keys you have to go around your persistence solution and re-encrypt a lot of the data, which is not ideal. Besides, having layers of encryption that allow that a single leakage from the storage keys will not grant access to plain data. Use different keys for storage and transport. Re-encrypting on read is easy and saves you from spreading keys that you want as tight as possible.
- Need to know Basis: Make sure that your keys (besides having a TTL tailored as much as possible to the targeted use of each key) allow the minimum possible access. This might end up creating complex permission schema but greatly reduces the impact area of data leakage too, not to mention is a security best practice in general.
Most of this is common sense, and what is useful for me might not be for you. Although I regularly challenge these suppositions on every service I write, the spy analogy and the guidelines I have formulated have served me well as a stable foundation on which to begin my thinking, always bearing in mind that security must be part of the DNA of my software.
5 Tips to Secure Your Microsevices Like an International Secret Agent was originally published in ShiftLeft Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.
This is a Security Bloggers Network syndicated blog post authored by Horacio Duran. Read the original post at: ShiftLeft Blog - Medium