Handling payment card information (PCI) involves complex regulations and a lot of paperwork. Luckily using AWS PCI Payment Solution with Serverless makes our job far easier.
In this article we’ll be looking at:
- What those requirements are
- How building with Serverless and AWS can reduce your workload
- Design patterns and processes to help you pass PCI compliance
- Reviewing the remaining responsibilities
PCI and those Requirements
When you enter your card details online, you want to make sure that those details are secure. The PCI requirements aim to enhance global payment account data security by developing standards.
There are a group of 12 PCI DDS requirements that you must meet to achieve PCI Compliance for your solution. Each one contains many sub-clauses and completing even the simplest PCI certification requires a lot of work and a really solid architecture.
- Protect your system with firewalls
- Configure passwords and settings
- Protect stored cardholder data
- Encrypt transmission of cardholder data across open, public networks
- Use and regularly update anti-virus software
- Regularly update and patch systems
- Restrict access to cardholder data to business need to know
- Assign a unique ID to each person with computer access
- Restrict physical access to workplace and cardholder data
- Implement logging and log management
- Conduct vulnerability scans and penetration tests
- Documentation and risk assessments
So why use Serverless and AWS?
When building a solution, you want to actually build your solution.
You don’t want to be spending all of your time reading PCI regulations, designing processes to meet the regulations, and then writing it all into compliance documentation.
When you work with a cloud provider, they’ve done a lot of the work already. AWS has a list of PCI compliant services.
Here is an infographic showing where the responsibilities lie when building a system with EC2s.
As you can see, AWS will handle the physical security and hardware configuration, but the rest is up to you.
If you were to go with Serverless then the story is quite different…
With Serverless services, AWS do a lot for you. They manage resourcing, the runtime environment, container isolation and much more.
This means that all of those compliance requirements also fall under their responsibility.
You still have a good set of requirements that you need to meet on your own, but they’re just over half what it could have been.
Design Patterns and Processes to Tackle the Rest
Now that you’ve chosen to build your solution using Serverless services, you might think that the rest will be easy.
Unfortunately, there are some very tricky requirements.
Luckily we have some lovely design patterns and systems that you can put in place to help tackle them and reduce your work.
- Multi Account AWS Setup
- Using STS Assume-Roles
- Multiple Environment Accounts
- AWS Single Sign-On
Then we’ll be looking at some specific Services and Tools for your Tech Stack
- Amazon Cognito, CloudWatch & CloudTrail
- DynamoDB
- Lambda
- The Serverless Framework
- IAM Least Privilege
- Static Code Analysis
- Runtime Event Analysis
Multi-Account AWS Setup
When being assessed for PCI compliance, everything that could possibly have access to card data is classed as being in-scope and is subject to the full requirements.
If you can prove that a section of your solution has no possible way to access the card data then is classed as out-of-scope.
With your whole solution in scope, everything has to follow the strictest of rules. For example, a developer wouldn’t be able to add a new dependency to the project without reviewing it and fixing every possible vulnerability. This slows down the development of your whole solution, not just the parts that handle card data.
Luckily, you can architect your accounts in a way that proves that only parts of your solution are in-scope is relatively easy. We call this de-scoping parts of your system.
Just remember when setting this all up that you should be using a single AWS organization to contain all of your accounts. This makes billing and organisation much easier
To de-scope large sections of your solution you can use a setup like this:
- Each environment has (at least) two AWS Accounts.
- One account contains just the resources that will come in contact with card details.
- One (or more) accounts that only ever reference card details by a reference key.
Architecting your solution in this way means that your card data will always be secure and compliant but the rest of the app doesn’t need to follow such strict rules.
This means your team can use the packages they want, access resources through the console and deliver features more easily.
Using STS Assume-Roles
There will always be situations where resources in your non-PCI environment need to know information stored in the PCI account. This could be the status of a payment, expiry date on the card or a billing address.
To access this information, a Lambda in the non-PCI account could use an STS assume-role to trigger a Lambda in the PCI Payment Solution with Serverless account. The triggered Lambda could retrieve the data and return it (as long as it doesn’t include PCI Data).
There are two requirements for this:
- This STS assume role will have very limited permissions, allowing it to trigger a single Lambda.
- The triggered Lambda needs minimal permissions and has to be closely assessed to ensure that there isn’t a data leak vulnerability.
As long as you do both of these things, this pattern for data access can be very powerful and keeps the non-PCI account out of scope.
Multiple Environment Accounts
Another trick to do with AWS Accounts is to have a set of accounts per environment.
This sounds like a lot of effort for something that could be done just with resource_${environment} but separating by AWS accounts gives you much more.
As with the separate accounts for PCI and non-PCI, resources in different environment accounts have no access to resources in another account unless explicitly given.
With different environment accounts, this means there’s no way that some tests you’re doing on staging can affect the production service.
Having completely separate environments can also help
Separating your environments also helps reduce human error as if you’re working in the development account, you can’t accidentally delete a production database.
And user access brings us nicely onto our next topic which is single sign-on.
AWS Single Sign-On
Now that we have multiple AWS accounts for PCI Payment Solution with Serverless and non-PCI services, and then have duplicated those accounts for each environment we need a way of managing who can do what in which account.
User access is also a requirement of PCI so it’s two birds with one stone.
AWS provides a service called single sign-on (AWS SSO) which allows you to give your AWS users a single place to sign in and it also gives your administrator a single place to manage who can do what in all of your accounts
You manage your user access by creating groups and permission sets. These can be combined to give really fine-grained access to each of your AWS accounts. It also can integrate with your existing active directory so that your users can sign in with their company credentials.
The process is pretty simple:
- Set up AWS SSO and then create users and assign them to groups (or connect your existing Active Directory with all of your users and groups)
- Create a Permission set that you want a group to be able to use (admin-full-access or developer-read-only) using IAM
- In AWS SSO, select an account that you want give access to
- Choose the group/s that you want to access this account
- Select the Permission set that you want the group/s to be able to use
Repeat steps 3-5 as many times as needed until every group has the right level of access to the correct accounts.
Also remember that you want as few people as possible to have access to the production PCI account. You can even design it in a way that no one ever needs access to the account.
Here you can see the access for an account. There are two different groups who have can use different permission sets.
This is also a really nice user experience for your team. They log into a single location and then can easily access all of the accounts they need to.
Services and Tools for your Tech Stack
Now we’re moving on to the more technical side of things. We’ll be looking at some services and tools that can really help make PCI Compliance a lot easier to achieve.
Amazon Cognito CloudWatch and CloudTrail
Two of the remaining requirements are to:
- Have authorization controls for anyone accessing applications and data
- Log and maintain audit trails of all access to applications and data
Amazon Cognito provides a really simple way of storing users, authenticating them, and controlling the access they have.
This is all built into a really easy-to-use service that will help you meet the first of these requirements.
Cognito also works really well with CloudWatch and CloudTrail which are Solutions that allow you to log and audit access to your applications and data.
With these three services in place, you’ll be able to monitor exactly who accesses what data.
These logs can automatically be transported to a central location for monitoring. At this point, you can connect your monitoring tools of choice.
Logs also need to be stored for at least one year so they can be stored in S3, and then lifecycle events can be used to move them to S3-Glacier for long-term archive storage after 30 days.
DynamoDB
Another of the requirements for storing card data, as well as a best practice for all of your customer data, is to have it encrypted.
Encrypting all of your data, managing the keys and rotations can become a large and complex task. It’s an ongoing overhead that can become an unwanted burden on your team.
With DynamoDB, encryption comes by default which can save your team a massive amount of time and effort.
It also has other benefits such as massive scalability, redundancy and it works beautifully with the next tech on this list. This makes it my go-to choice for database storage in the cloud.
Lambda
AWS Lambda is the backbone of Serverless development. It provides you with an incredible amount of computer power whilst not charging you a penny when it’s not running.
Not having to think about implementing autoscaling, redundancy, IP addresses and networking can save your developers a chunk of time.
Each Lambda is also isolated from all of the other Lambdas in your AWS accounts. This makes it much more secure and allows you to control its permissions with much greater granularity. We’ll be talking about that a little bit later.
The Serverless Framework
With all of these services, we need a nice way of managing and controlling it all.
For that, a great option is the Serverless Framework this is an open-source tool that allows you to easily create solutions using the services have already talked about.
It does this by allowing you to define your infrastructure using code. Creating your infrastructure this way makes it easier to analyse and more reliable to reproduce.
This also now acts as self-documentation, explicitly stating what is deploying to which account. This ticks off one more of the requirements as well as creates a great development experience.
The way that the Serverless Framework works is that it takes your infrastructure definitions and turns them into CloudFormation templates. These are YAML or JSON files that AWS knows how to read. When these are loaded into an AWS account, CloudFormation will deploy whatever has been defined in the template.
You could write all of the CloudFormation templates your self but the Serverless Framework adds a layer of abstraction and really simplifies the process.
The Serverless Framework also has a huge community of users who have built an amazing list of plugins. These can improve your developer workflow, reduce repetitive code, make configuring complex services much simpler and much more.
Some of my favourite plugins include serverless-offline
with serverless-dynamodb-local
for running your solutions locally, serverless-dynamodb-autoscaling
if you use provisioned DynamoDB, and serverless-iam-roles-per-function
for making IAM Least privilege much easier.
IAM Least Privilege
Using the serverless framework also enables you to control exactly what permissions each and every bit of code has. You can create a custom IAM policy for each It can do on each of your resources.
This process is called granting the least privilege and is yet another of the requirements that are needed for PCI compliance.
In your function definitions, you can define the exact permissions that the Lambdas will have.
When you do this for each of your Lambda you can explicitly show that this Lambda doesn’t have access to the ‘CardDataTable’ and can’t invoke another function that does. Having this explicit access will make documenting and meeting the PCI requirements much easier.
Static Code Analysis
The serverless framework generates CloudFormation templates which are then used to create the infrastructure on your AWS accounts. We can add a layer of security by analyzing these CloudFormation templates to ensure that they comply with the PCI regulations and best practices.
There are lots of tools that can help you with this. They allow you to define the exact rules that you want to test against. One example is CFN-NAG (short for CloudFormation Nag) which can be integrated into your development process as well as your CI/CD processes.
When running CFN_NAG it will be looking for all of these things, ensuring that PCI requirements and best practices are followed. You can also select which rules you want to follow or even add your own custom rules. By default it will look for:
- IAM rules that are too permissive (wildcards)
- Security group rules that are too permissive (wildcards)
- Access logs that aren’t enabled
- Encryption that isn’t enabled
- Password literals
There are also tools that you can use to scan the code that will be running inside your Lambdas. These tools can do things such as checking for passwords, insuring PCI data is not your logs.
This static analysis of both the code and the CloudFormation templates is another requirement of PCI Payment Solution with Serverless.
Runtime Event Analysis
With a traditional architecture, your code will largely be triggered through endpoints on your server. When you’re running a truly serverless solution there will be a huge range of different services that are triggering a Lambda.
Your Lambda could be triggered by a DynamoDB change, a file uploaded to S3, or it could be directly invoked by another Lambda. In these situations, a traditional firewall or even a web application firewall (WAF) just won’t cut it.
With API Gateway you provide request/response data mappings that can help sanitize the range of possible inputs, but this only works for Lambdas behind an API Gateway. We want a solution that will work for all Lambdas.
You need a way of ensuring that the event that is triggering the Lambda isn’t malicious, and this can only happen actually within the Lambda.
There are different ways of doing this each having its own benefits and drawbacks. Here are the two most common.
The first and probably the simplest is to define the schema of the expected event and validate all events that are received against that schema. This solution is great as it is easy to understand and there is an abundance of services out there that you can use for this.
YUP is a package that is very commonly used in the JavaScript world for validating objects and can be easily added at the start of every Lambda function ensuring that the event payload is at least in the right format.
The drawback to this method is that there is the possibility of someone maliciously putting content into that event object so that it still meets the required schema. You could create further tests for this if your security specialist says that it’s a requirement.
Another solution is to use a service that provides you with a package that will scan every event for malicious content. This will cover both malformed events and malicious content that is embedded in the correct form.
Review
Let us go back to the diagram that we looked at at the start of this article. We can review which of the requirements we have met with the Solutions we have discussed.
The authentication of users, as well as the authorization controls for accessing applications and data, have been met with Cognito and AWS Single Sign-On.
CloudWatch and CloudTrail Help us meet the log and audit trail requirements
The YUP schema analysis or paid security solution meets the firewall and event data inspection requirement.
And finally using the Serverless Framework with the plugins and tools we’ve talked about will meet the requirements for least privilege IAM, application behaviour, code scanning and configuration, as well as the requirement to maintain a full cloud asset inventory.
This setup will also provide a solution for gathering and transporting all of your logs. From there you can easily monitor errors and security incidents. I’ve chosen not to discuss specific tools for monitoring as there are a lot of tools out there are and everyone has their own preferences.
To meet the data leak protection requirement you’ll still need to architect your application effectively, but the DynamoDB encryption, IAM least privilege, account isolation, and the single sign-on will tick a lot of the boxes already.
This leaves the responsibility of detecting and fixing vulnerabilities in third-party dependencies and removing obsolete Cloud services. These are tasks that will need to be continuously completed throughout the development cycles of your solution.
If we try and compare are these responsibilities to the responsibilities you would have had if you went with a more traditional architecture the time, effort and resource savings become very apparent.
Sam Williams
Complete Coding Director
Sam@CompleteCoding.io