AWS Serverless Common Mistakes - Security (6/7)

With serverless, there are fewer security issues because you manage less of the system. But there still are a few issues. This is part of a multipart blog post about serverless common mistakes. Friday, February 15, 2019

This is continuation of a blog about common mistakes when building serverless systems on AWS. It is split into the following parts:

With serverless, we do not take care of infrastructure and also do not take care of any related security problems. But that doesn't mean the application is secure. All of the OWASP Top 10 still apply. You must take care of SQL injection (if you use SQL), XSS, CSRF, and so on, because this is part of your application. You can use a web application firewall (WAF) provided by AWS, but no system can fully protect you from bad code and bad practice. Most of bad practice comes from code samples that ignore security to simplify the code. That is not a problem if you are aware of this.

Jeremy Daly wrote an excellent blog about serverless security.

Top 10

PureSec made a comprehensive guide that consists of the 10 most important risks and challenges regarding serverless security.

@PureSecTeam wrote 10 most important risks and challenges regarding serverless security

Principle of Least Privilege

Settings privileges are mostly done by IAM roles. As in every system, including serverless, you should set minimum required permissions. That means setting IAMs for each function separately and only permissions they need. You should not use asterisk characters like "s3:*" as this grants all privileges to S3, which you probably do not need if you only read data.

Do not use asterisk in IAM policy like s3:*

Do Not Save Secrets in Environment Variables or Code

Do not save secrets in environment variables or code. They are checked into the source repository and then difficult to protect. You should use System Manager Parameter Store with an additional feature to encrypt variables with KMS. Serverless Framework supports this very simply:

Set variable in serverless.yml:

provider:
  environment:
    MYSECRET: ${ssm:/myapp/mysecret~true}

And use it in code (JavaScript):

const mysecret = = process.env.MYSECRET;

Then we go to System Manager -> Parameter Store and set value of parameter "/myapp/mysecret". We select SecureString to save the encrypted value to KMS. Data is automatically decrypted at deploy time if you set "~true" in serverless.yml. If you want to dynamically read the value you must implement it yourself. If possible, store it in a global variable so it can be reused between multiple function invocation.

For partially important secrets, you can use a plain string in the System Manager Parameter Store without encryption with KMS (KMS is not free). You could also have noncritical secrets in a separate file, which you exclude from GIT. This is simply done in Serverless Framework. You just reference a YAML or JSON file:

custom: ${file(../myCustomFile.yml)}

and then use it

${self:custom.globalSchedule}

This is clearly documented here.

Do not save secrets in environment variables or code

Another great blog about AWS Lambda and Secret Management.

Set Cross-Origin Resource Sharing (CORS) to Appropriate Value

When building HTTP API with API Gateway, you can enable CORS. With a CORS setting, you notify the browser that can access the resource even if the web page is from a different domain. The setting only applies if API is accessed from the browser. The problem is that most tutorials just instruct you to enable CORS, which means enabling for every domain. You should restrict it only to the domain from which you plan to use your API. You can read more about setting CORS here.

Do not use * in CORS settings if not necessary

Beware of Third-Party Libraries

There have been many security breaches in the past in the Node.js package manager (NPM). Read about these breaches here and here and here. However, NPM in not the only one. Any external library presents a potential risk. Node.js is more exposed because developers are used to including a lot of packages and they usually have a lot of dependencies. For very simple projects, you can easily exceed 600 packages that you include or subsequently depend on. You can read here or watch this video on how easy it is to compromise NPM packages.

It is very easy to compromise NPM packages!!!

Secure SQL Connection

If you setup a public connection to SQL for administrative or developer access, limit IP addresses and use SSL. When connecting to SQL with Lambda, also use SSL; especially if you have public access to avoid using Lambda in VPC (not recomended).

How to enforce SSL:

  • PostgreSQL: rds.force_ssl=1 in the AWS RDS Console (Parameter Groups)
  • MySql: Within the DB: GRANT USAGE ON *.* TO ‘mysqluser’@’% REQUIRE SSL;

To connect using SSL, provide the root certificate that you get from AWS, enable the SSL connection, and verify the SSL certificate. Instructions for each database can be found here.

Enforce SSL when connecting to SQL on AWS.

Tools for the rescue

FunctionShield from PureSec enables you to limit what Lambda can do. You can limit outbound internet connectivity. Disable read and write to /tmp/ directory. Disable child process execution. Prevent source code leakage.

FunctionShield from @PureSecTeam enables you to limit what Lambda can do

Another utility from PureSec serverless-puresec-cli enables you to auto-generate the least privileged roles for AWS Lambda. But double check the generated privileges. You do not want to generate privileges by already security breached code.

Services to the rescue

Chris Munns, Principal Developer Advocate for Serverless, recommend turning on CloudTrail, Config and CloudTrail Data Events which help you monitor your system. These services are not free, but so are not free security breaches or other system malfunctions.

Use CloudTrail, Config and CloudTrail Data Events to monitor your serverless system