Information security (infosec) is a broad field. Its practitioners behave more like artists than engineers. After all, the mandate for security is not "do X", but instead "ensure no one can do X, Y, Z, ɑ, β, ɣ, etc.".
The array of possibilities leading to infosec failure are vast. It's like trying to prove a negative, thus making the task near impossible.
On one hand we have an impossible task, on the other we have the affordance of time. In one sense we have been practicing infosec since the invention of the Caesar Cipher in 50 BC.
Yes, software infosec is younger, but we benefit from decades of development in the field. Modern application designs are often developed with infosec in mind. For example, newer services have finer-grained access controls, stateless connections, and time-based authentication.
Cloud applications have three core infosec needs:
Modern applications are constructed via collections of managed services. (Aside: If you are operating off-the-shelf databases or similar services yourself, you are not really doing modern application development.)
Every interaction between managed services crosses a service boundary. These service boundaries are where we manage access controls. For example, if a Lambda Function wants to interact with a DynamoDB Table it needs to be granted access to the table.
Let's look at an example:
Resource-Level Access Controls are granted in AWS via IAM statements. IAM statements are often attached to compute resources needing access to other resources. In the above example, we are adding permission for a Lambda Function to create, read, update, and delete items inside the table.
The example shows permissions granted using SAM policy templates, which make specifying permissions easier. You start by declaring your intent (CRUD access to table items) instead of specifying each individual action needed, and you end by declaring the specific table you want to access instead of having to craft a complex identifier called an AWS ARN.
These two concepts seem simple, but there are still many innovative developments underway to provide more logical mechanisms to specify them. In the example above we specified the scope of resources to be limited to the specific Table connected to the Function. This is great for many application resources. For human-identity controls it may be better to scope resources based on tags (otherwise known as Attribute Based Access Control or ABAC). This allows for policies like granting access to all Tables that are tagged with "Environment: Development".
When using a fully-featured cloud provider like AWS, most applications can be implemented using cloud provider offerings alone.
For example, a three-tier application can be constructed using an API Gateway HTTP API, some Lambda Functions, and a DynamoDB Table. Yet, some application use cases must be met using services from third parties. These services are accessed using credentials that are not managed automatically by the cloud provider.
Third-party service credentials are usually private and confidential. The data should be:
Cloud providers have services for managing secrets (e.g. AWS Secrets Manager). These are key/value stores where the key is the logical identifier for a secret and the value is the confidential data. These stores also have auditing mechanisms built-in and offer more flexible capabilities for defining the scope of access.
Often, a human identity uses write access to create or update a secret. And an application resource uses read-only access to retrieve the secret at runtime.
Sometimes even first-party cloud managed services require secrets. Relational databases like Aurora Serverless are an example of this. The most secure architecture for these solutions involves letting AWS manage the credentials, generating them cryptographically when creating databases, and then referencing credentials without ever accessing them when querying the database.
When the compute resource queries the database it does so by passing along the identifier of the credentials in AWS Secrets Manager without ever retrieving the credentials directly:
aws rds-data execute-statement \ --resource-arn "arn:aws:rds:us-east-1:123456789012:cluster:mydbcluster" \ --database "mydb" \ --secret-arn "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/dbcreds" \ --sql "select * from mytable"
This ensures the compute resource never stores the secret locally.
When an application resource needs to use secrets, it should:
AWS Secrets Manager has built-in mechanisms to rotate credentials for many AWS managed databases. AWS Secrets Manager further enables custom rotation logic for other first-party and third-party services. Automated mechanisms enable frequent rotation, decreasing the chance that a leaked secret will be usable by a malicious third party.
Sometimes the right architecture for an application involves managed services that predate modern infosec practices.
Relational databases are one example of such resources. While we may want to rid our applications of relational databases (for many reasons), there is too much value built up in existing application code and libraries to justify banishment of these database types. Instead, it's simpler to use alternative infosec mitigation strategies that can be just as effective (though a little more cumbersome to architect).
If you can build on or migrate to an Aurora Database Cluster with a managed Data API service, then you should try to use its vastly superior access controls.
If that's not an option, things get a bit trickier. Previous access models involve establishing stateful TCP connections to databases. These connections are authenticated using static username/password credentials. They are optionally configured to encrypt data in transit (but often are not configured to do so by default). Further, each stateful connection adds overhead and limits the scalability of databases. They present an opportunity for a DoS attack if a third party is able to locate a database service even if they don't have credentials to authenticate.
The best solution to ensure security in the face of these challenging conditions is to prevent access to databases from the public internet.
The easiest way to lock down access is to put the database into a virtual network, like AWS's Virtual Private Cloud service. Databases in these networks can be given private addresses that are not publicly reachable. But this also means compute resources that need to interact with the databases must be able to access the private network.
One further complication results from using a virtual network. Private resources are by definition not accessible from the public internet, which can make it difficult to interact with them for development, testing, or debugging purposes.
We need a way to create a secure tunnel between ourselves and the private resources in the virtual network. There are multiple ways to create private tunnels, but one of the most cost-effective solutions is running an SSH Bastion Server.
Traditional SSH Bastion Servers also require static credentials, either username/password or public/private key authentication. The ideal authentication mechanism uses time-based credentials. Thankfully, AWS provides the Systems Manager Session Manager service for establishing SSH connections and tunnels to Bastion Servers. It can be used with tools like ssm-tool to authenticate and establish connections using single-use, temporary public/private keys.
When an application resource requires stateful connections and/or static credentials, use the following techniques to securely provision and access them:
While breaking down security solutions based on use cases and resource needs is an important step towards developing the right infosec application architecture, a holistic approach should also identify how humans working in teams collaborate to accurately reproduce these architectures across various projects. It's important to empower developers with the right tools that let them work at a human logic level rather than a machine logic level.
Stackery's platform enables teams to automate some infosec best practices. Get started today building well-architected applications.