AWS Security Baseline provides a checklist of security practices and steps that should be part of every AWS application - no excuses.
This document is meant to be updated and improved over time.
In engineering, everything is about the trade-offs. That's why at the beginning of an MVP development everyone has access to production environment and database. Then, as the product grows, gets users, we can tighten the security aspects more, restrict access to resources, introduce additional roles and so on.
It's needless to say that it does not make sense to have a fortress-like security setup for the product that does not have any users. That's what sane defaults are all about.
Let's see what we consider Security Baseline for AWS - this is what we implement from day one.
AWS account setup utilises AWS Organization and follows multi-account setup described in the best practices documentation.
Of course, that does not mean that we should have 5 different environments from day one. But having a separate management and billing account, security (where all the IAM users are defined) and production accounts is a good start.
Multi-factor Authentication is also necessary for root accounts.
A database is a heart of every software. No one wants their heart exposed to the world. It's dangerous world out there. Even though not leaving the database open to the world seems like a common wisdom, there are thousands databases the are exposed. How that happens? I don't think that the case is that the engineers do not know that a database needs to be protected. More likely is that a proof of concept phase moved to production too quickly and no one remembered to set up the database authentication and configure the firewall. That's what we want to achieve by having a checklist.
Backend services are behind a load balancer and have firewall configuration (security groups) so that they listen only on designated ports and can only be accessed from the load balancer. Why? We don't want to allow SSH-ing into our backend services from anywhere, there's rarely a reason to ping or invoke a production backend service from anywhere except for load balancer. The goal is to keep attack vector as small as possible.
If possible, offload user authentication and authorisation to AWS Cognito. Also, do not try to implement your own encryption library.
One of the infamous security breaches at Facebook a while ago happened because before the passwords being hashed and stored in the database (hashed, as it should be), the plain text passwords were logged (most likely for testing/debugging purposes) and stored on log servers.
Both encryption in transit (HTTPS) and encryption at rest are so easy to set up, come with very little performance and maintenance overhead, and are almost free that there is absolutely no reason not to do it correctly.
Is there something we missed or got wrong? Let us know!