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.
Top comments (5)
Using the
~/.aws/credentials
approach is useful for a development environment where you potentially need to test things with multiple profiles, but for a production deployment, the ENV Variable is preferred, right?Another important consideration: Don't check credentials into VCS in plain text. It may not seem to bad if the team is small and the repo is private, but eventually, the team will grow and it will be necessary to limit access to the credentials!
Good point with the VCS! itโs very important never commit credentials.
If you use a CI/CD tool like CircleCI or Jenkins you can store the credentials as environment variables so the Containers in the build process have access to them.
So yes for not local environments itโs a good pattern to use the ENV variable.
You make a great point here. In fact, with IAM roles, these environment variables are set which means reading credentials from environment variables is the way to go. I will clarify these in the post ๐๐ผ
Hi,
I have a react js app, which requires images to be uploaded in S3 bucket. Since it is a react app I cannot install the AWS credentials in the environment variables because they will not be accessible from client browser.
Can you please suggest the right way for storing the credentials in such scenario.
I never really got why they require us to put credentials in ~/. For me, what's for a project stays in the project directory. I don't like everything spreading out of place.