Best practices to use AWS access key and secret in your development environment
Sanjeet Sahay
Posted on May 17, 2020
If you are an AWS developer and are using AWS services in your app, then you must have found yourself looking for the best way to securely store and access your AWS credentials. To keep our AWS account secure, it's important for us to understand the AWS shared responsibility model.
In a nutshell, it states that AWS is responsible for the security of the cloud and us, the customers, are responsible for the security in the cloud. Simply put, for developers, it means that we should take special care of our AWS credentials like Access key ID and Secret Access Key.
If you are new to AWS, use the references section below for more information.
1. Anti-pattern: Hardcoding credentials
This is an anti-pattern and must be avoided at all costs. If your code looks like the following then you must act now
const AWS = require("aws-sdk");
AWS.config.update({
credentials: {
access_key_id: "<your-access-key-id>",
secret_access_key: "<your-secret-access-key>"
}
})
1.1. Why is this bad?
As a developer, you are most likely to commit this code in some repository like a private GitHub repo or your team repository such as BitBucket or AWS CodeCommit. Besides running a risk of using an anti-pattern, you don't want someone to access your hard-coded keys, because it will allow them to access/manage all the resources that these credentials provide access to. If the IAM policy attached to the user whose credentials you are using looks like the following, it means that you have handed over the keys to your AWS kingdom to anybody who has access to your code
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
}
1.2. How do I mitigate?
If you think that you can't make changes to your code, then you must modify the IAM policy attached to that role or move them to an IAM group with restrictive privileges e.g. IAM policy that grants least privileges to only a given Amazon S3 bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListYourObjects",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": ["arn:aws:s3:::bucket-name"]
},
{
"Sid": "ReadWriteDeleteYourObjects",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": ["arn:aws:s3:::bucket-name"]
}
]
}
2. Look ma, no hardcoded credentials
With that anti-pattern out of the way, you may take one of the following approaches to use your AWS credentials.
2.1. Use environment variables
$ export AWS_ACCESS_KEY_ID="<your-access-key-id>"
$ export AWS_SECRET_ACCESS_KEY="<your-secret-access-key>"
then, in your JavaScript/Node.js app, use the following
const AWS = require("aws-sdk");
AWS.config.update({
credentials: {
access_key_id: process.env.AWS_ACCESS_KEY_ID,
secret_access_key: process.env.AWS_SECRET_ACCESS_KEY
}
})
2.2. Use AWS profile
You can use AWS named profiles to store more than one credential. You can inspect the following two files:
-
~/.aws/credentials
: containsaws_access_key_id
andaws_secret_access_key
-
~/.aws/config
: containsregion
andoutput
My ~/.aws/credentials
file looks like the following and it shows that I am using 2 profiles: default
and personal
[default]
aws_access_key_id = "<your-access-key-id>"
aws_secret_access_key = "<your-secret-access-key>"
[personal]
aws_access_key_id = "<your-access-key-id>"
aws_secret_access_key = "<your-secret-access-key>"
My ~/.aws/config
file looks like the following:
[default]
region = us-west-2
output=json
[profile personal]
region = us-west-2
output = json
If I want to use my default account, I can use the following code:
const AWS = require("aws-sdk");
const credentials = new AWS.SharedIniFileCredentials({ profile: "default" });
AWS.config.credentials = credentials;
What about my code running in Amazon EC2, AWS Lambda?
I have 3 words for you: "Use IAM roles".
If you have your code running in a Docker container on an Amazon EC2 instance, then understand that every single process on the system has access to IAM roles and your container will assume that role without you having to specify it.
Conclusion
For my development environment, I have found the latter approach of using AWS profiles and using them to access credentials in code better than having to export it. The code is much cleaner and doesn't change if I rotate my keys. All I need to do is to run aws configure
on my developer workstation and be done with it. Also, it saves me from the anti-pattern of hard-coding credentials in my code. However, this approach means that you may have to change the code or write conditional code (rarely a good practice) or use environment specific files for your non-development environments where the profile may or may not exist. If you run into such a decision making process, using the environment variable approach is the way to go.
References
Posted on May 17, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.