DEV Community

Sanjeet Sahay
Sanjeet Sahay

Posted on

Best practices to use AWS access key and secret in your development environment

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.
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>"
 }
})


Enter fullscreen mode Exit fullscreen mode

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": "*"
  }
}


Enter fullscreen mode Exit fullscreen mode

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"]
        }
    ]
}


Enter fullscreen mode Exit fullscreen mode

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>"


Enter fullscreen mode Exit fullscreen mode

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
 }
})


Enter fullscreen mode Exit fullscreen mode

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: contains aws_access_key_id and aws_secret_access_key
  • ~/.aws/config: contains region and output

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>"


Enter fullscreen mode Exit fullscreen mode

My ~/.aws/config file looks like the following:



[default]
region = us-west-2
output=json

[profile personal]
region = us-west-2
output = json


Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode




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

Top comments (5)

Collapse
 
sidpalas profile image
sidpalas • Edited

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!

Collapse
 
marcellusherlus profile image
marcellusherlus

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.

Collapse
 
sahays profile image
Sanjeet Sahay

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 ๐Ÿ‘๐Ÿผ

Collapse
 
alikhan profile image
Ali Hassaan

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.

Collapse
 
a2br profile image
a2br

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.