whispering

Your Serverless Function has a Secret

Your serverless function has a secret... maybe it's a password for a remote API, a private key, or signing certificate. These secrets have to be stored somewhere, and in the old days that usually meant just a plaintext config file on your server. Sure, you could encrypt it, but then you have to put the key on the server, and you haven't gained anything except a bit of obfuscation. Or you could use more complex schemes, like Hiera-Eyaml, which is a small improvement, but you've really just moved the threat to a different part of your infrastructure.

Using AWS KMS (Key Management Service) with AWS Lambdas offers a significant reduction in attack surface area. You get HSM-like functionality, without the high cost (starting at $1/month). In this model, there are only three ways the secret can be accessed:

  1. From inside the container running the Lambda function code
  2. By an IAM user with admin rights - and you protected these accounts with two-factor auth, right?
  3. Amazon screws up catastrophically

I believe (3) is important to acknowledge here - the risk doesn't go away, but is outsourced to an extremely mature, security-focused organization with a stellar track record and a lot at stake.

Here's a short example using the Serverless framework, and Node.js. Many other language and framework combinations are possible.

Here are the steps. Refer to the quickstart docs for Serverless, KMS, and aws-cli if any of these are unfamiliar.

  1. Create a KMS key in the AWS console, and make a note of its ARN.
  2. Generate some ciphertext with the following CLI command:
    aws kms encrypt --key-id 'arn:aws:kms...' --plaintext 'secret'
  3. Add the ciphertext as an environment variable
  4. Add the ARN to the awsKmsKeyArn property in your serverless.yml file
  5. Update your code with kms.decrypt() to access the secret

index.js

'use strict';
const aws = require('aws-sdk');
var kms = new aws.KMS();
 
exports.hello = (event, context, callback) => {
  kms.decrypt({CiphertextBlob:
    new Buffer(process.env.password, 'base64')},
    (err, data) => {
 
    var password = data.Plaintext.toString('ascii');
 
    /*
     * Do stuff with plaintext password
     */
 
    callback(null, {
      statusCode: 200,
      body: JSON.stringify("hello"),
      headers: {
          'Content-Type': 'application/json',
      },
    });
 
  });
};

serverless.yml

service: example

provider:
  name: aws
  region: us-west-2

functions:
  hello:
    handler: index.hello
    events:
      - http:
          path: hello
          method: get
    awsKmsKeyArn: 'arn:aws:kms:us-west-2:123456789:key/80abc534-be62-992a-7ba1-e3a44d4271ac'
    environment:
      # password value below is KMS ciphertext
      password: 'AQICAHiNrBUmBQhi496ozpFzP9BrHUALq+NAyE7Xsz6gRsiz0AGsJnI1cyYl57qLnxn1FDSkAAAAbTBrBgkqhkiG9w0BBwagXjBcAgEAMFcGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM8TW0l6r7UEi1soz9AgEQgCp4Vy6sWq1W7SXXmeaz3jRL/X5ax4E2Uw3N0KGztPwv9kGK8O2Nk56tM4Q='
    runtime: nodejs6.10
Date posted: June 1, 2018

Comments

Is it safe to add both awsKmsKeyArn and encrypted password in the serverless.yml and make it visible on github?

The Arn and password are useless outside of your environment. Basically like the author said, the only way someone can leverage those values is if AWS itself is compromised.

You could skip steps 1 through 4 by adding your secret key to SSM Parameter store and loading them directly into your template like this:

environment:
password: ${ssm:/${self:provider.stage}/config/password}

You could append ~true to decrypt it at bundle time but this will decrypt the variable when the project is packages and placed them in the Cloudformation template as well as in the Lambda Environmental variables.

I think it's better to keep them encrypted and decrypt (kms.decrypt) them inside your Lambda handler.

Not implemented in lambda, so for lambda user i suggest building a layer with the variables via continuous integration

What if you have several password stored in kms and used by your lambdas ?
Here you set the key "awsKmsKeyArn" on your serverless.yml but i don't get how to do with several... because "awsKmsKeyArn" and password hash are linked to the same key...

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>, <cpp>, <java>, <php>. The supported tag styles are: <foo>, [foo].
  • Web page addresses and email addresses turn into links automatically.
  • Lines and paragraphs break automatically.

Metal Toad is an Advanced AWS Consulting Partner. Learn more about our AWS Managed Services

Schedule a Free Consultation

Speak with our team to understand how Metal Toad can help you drive innovation, growth, and success.