# Serverless.com Security

{{#include ../banners/hacktricks-training.md}}

## Basic Information

### Organization

An **Organization** is the highest-level entity within the Serverless Framework ecosystem. It represents a **collective group**, such as a company, department, or any large entity, that encompasses multiple projects, teams, and applications.

### Team

The **Team** are the users with access inside the organization. Teams help in organizing members based on roles. **`Collaborators`** can view and deploy existing apps, while **`Admins`** can create new apps and manage organization settings.

### Application

An **App** is a logical grouping of related services within an Organization. It represents a complete application composed of multiple serverless services that work together to provide a cohesive functionality.

### **Services**

A **Service** is the core component of a Serverless application. It represents your entire serverless project, encapsulating all the functions, configurations, and resources needed. It's typically defined in a `serverless.yml` file, a service includes metadata like the service name, provider configurations, functions, events, resources, plugins, and custom variables.

```yaml
service: my-service
provider:
  name: aws
  runtime: nodejs14.x
functions:
  hello:
    handler: handler.hello
```

<details>

<summary>Function</summary>

A **Function** represents a single serverless function, such as an AWS Lambda function. It contains the code that executes in response to events.

It's defined under the `functions` section in `serverless.yml`, specifying the handler, runtime, events, environment variables, and other settings.

```yaml
functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
```

</details>

<details>

<summary>Event</summary>

**Events** are triggers that invoke your serverless functions. They define how and when a function should be executed.

Common event types include HTTP requests, scheduled events (cron jobs), database events, file uploads, and more.

```yaml
functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
      - schedule:
          rate: rate(10 minutes)
```

</details>

<details>

<summary>Resource</summary>

**Resources** allow you to define additional cloud resources that your service depends on, such as databases, storage buckets, or IAM roles.

They are specified under the `resources` section, often using CloudFormation syntax for AWS.

```yaml
resources:
  Resources:
    MyDynamoDBTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: my-table
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
        KeySchema:
          - AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
```

</details>

<details>

<summary>Provider</summary>

The **Provider** object specifies the cloud service provider (e.g., AWS, Azure, Google Cloud) and contains configuration settings relevant to that provider.

It includes details like the runtime, region, stage, and credentials.

```yaml
yamlCopy codeprovider:
  name: aws
  runtime: nodejs14.x
  region: us-east-1
  stage: dev
```

</details>

<details>

<summary>Stage and Region</summary>

The stage represents different environments (e.g., development, staging, production) where your service can be deployed. It allows for environment-specific configurations and deployments.

```yaml
provider:
  stage: dev
```

The region specifies the geographical region where your resources will be deployed. It's important for latency, compliance, and availability considerations.

```yaml
provider:
  region: us-west-2
```

</details>

<details>

<summary>Plugins</summary>

**Plugins** extend the functionality of the Serverless Framework by adding new features or integrating with other tools and services. They are defined under the `plugins` section and installed via npm.

```yaml
plugins:
  - serverless-offline
  - serverless-webpack
```

</details>

<details>

<summary>Layers</summary>

**Layers** allow you to package and manage shared code or dependencies separately from your functions. This promotes reusability and reduces deployment package sizes. They are defined under the `layers` section and referenced by functions.

```yaml
layers:
  commonLibs:
    path: layer-common
functions:
  hello:
    handler: handler.hello
    layers:
      - { Ref: CommonLibsLambdaLayer }
```

</details>

<details>

<summary>Variables and Custom Variables</summary>

**Variables** enable dynamic configuration by allowing the use of placeholders that are resolved at deployment time.

- **Syntax:** `${variable}` syntax can reference environment variables, file contents, or other configuration parameters.

  ```yaml
  functions:
    hello:
      handler: handler.hello
      environment:
        TABLE_NAME: ${self:custom.tableName}
  ```

* **Custom Variables:** The `custom` section is used to define user-specific variables and configurations that can be reused throughout the `serverless.yml`.

  ```yaml
  custom:
    tableName: my-dynamodb-table
    stage: ${opt:stage, 'dev'}
  ```

</details>

<details>

<summary>Outputs</summary>

**Outputs** define the values that are returned after a service is deployed, such as resource ARNs, endpoints, or other useful information. They are specified under the `outputs` section and often used to expose information to other services or for easy access post-deployment.

```yaml
¡outputs:
  ApiEndpoint:
    Description: "API Gateway endpoint URL"
    Value:
      Fn::Join:
        - ""
        - - "https://"
          - Ref: ApiGatewayRestApi
          - ".execute-api."
          - Ref: AWS::Region
          - ".amazonaws.com/"
          - Ref: AWS::Stage
```

</details>

<details>

<summary>IAM Roles and Permissions</summary>

**IAM Roles and Permissions** define the security credentials and access rights for your functions and other resources. They are managed under the `provider` or individual function settings to specify necessary permissions.

```yaml
provider:
  [...]
  iam:
    role:
      statements:
        - Effect: 'Allow'
          Action:
            - 'dynamodb:PutItem'
            - 'dynamodb:Get*'
            - 'dynamodb:Scan*'
            - 'dynamodb:UpdateItem'
            - 'dynamodb:DeleteItem'
          Resource: arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/${self:service}-customerTable-${sls:stage}
```

</details>

<details>

<summary>Environment Variables</summary>

**Variables** allow you to pass configuration settings and secrets to your functions without hardcoding them. They are defined under the `environment` section for either the provider or individual functions.

```yaml
provider:
  environment:
    STAGE: ${self:provider.stage}
functions:
  hello:
    handler: handler.hello
    environment:
      TABLE_NAME: ${self:custom.tableName}
```

</details>

<details>

<summary>Dependencies</summary>

**Dependencies** manage the external libraries and modules your functions require. They typically handled via package managers like npm or pip, and bundled with your deployment package using tools or plugins like `serverless-webpack`.

```yaml
plugins:
  - serverless-webpack
```

</details>

<details>

<summary>Hooks</summary>

**Hooks** allow you to run custom scripts or commands at specific points in the deployment lifecycle. They are defined using plugins or within the `serverless.yml` to perform actions before or after deployments.

```yaml
custom:
  hooks:
    before:deploy:deploy: echo "Starting deployment..."
```

</details>

### Tutorial

This is a summary of the official tutorial [**from the docs**](https://www.serverless.com/framework/docs/tutorial):

1. Create an AWS account (Serverless.com start in AWS infrastructure)
2. Create an account in serverless.com
3. Create an app:

```bash
# Create temp folder for the tutorial
mkdir /tmp/serverless-tutorial
cd /tmp/serverless-tutorial

# Install Serverless cli
npm install -g serverless

# Generate template
serverless #Choose first one (AWS / Node.js / HTTP API)
## Indicate a name like "Tutorial"
## Login/Register
## Create A New App
## Indicate a name like "tutorialapp)
```

This should have created an **app** called `tutorialapp` that you can check in [serverless.com](serverless.com-security.md) and a folder called `Tutorial` with the file **`handler.js`** containing some JS code with a `helloworld` code and the file **`serverless.yml`** declaring that function:

{{#tabs }}
{{#tab name="handler.js" }}

```javascript
exports.hello = async (event) => {
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: "Go Serverless v4! Your function executed successfully!",
    }),
  }
}
```

{{#endtab }}
{{#tab name="serverless.yml" }}

```yaml
# "org" ensures this Service is used with the correct Serverless Framework Access Key.
org: testing12342
# "app" enables Serverless Framework Dashboard features and sharing them with other Services.
app: tutorialapp
# "service" is the name of this project. This will also be added to your AWS resource names.
service: Tutorial

provider:
  name: aws
  runtime: nodejs20.x

functions:
  hello:
    handler: handler.hello
    events:
      - httpApi:
          path: /
          method: get
```

{{#endtab }}
{{#endtabs }}

4. Create an AWS provider, going in the **dashboard** in `https://app.serverless.com/<org name>/settings/providers?providerId=new&provider=aws`.
   1. To give `serverless.com` access to AWS It will ask to run a cloudformation stack using this config file (at the time of this writing): [https://serverless-framework-template.s3.amazonaws.com/roleTemplate.yml](https://serverless-framework-template.s3.amazonaws.com/roleTemplate.yml)
   2. This template generates a role called **`SFRole-<ID>`** with **`arn:aws:iam::aws:policy/AdministratorAccess`** over the account with a Trust Identity that allows `Serverless.com` AWS account to access the role.

<details>

<summary>Yaml roleTemplate</summary>

```yaml
Description: This stack creates an IAM role that can be used by Serverless Framework for use in deployments.
Resources:
  SFRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              AWS: arn:aws:iam::486128539022:root
            Action:
              - sts:AssumeRole
            Condition:
              StringEquals:
                sts:ExternalId: !Sub "ServerlessFramework-${OrgUid}"
      Path: /
      RoleName: !Ref RoleName
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AdministratorAccess
  ReporterFunction:
    Type: Custom::ServerlessFrameworkReporter
    Properties:
      ServiceToken: "arn:aws:lambda:us-east-1:486128539022:function:sp-providers-stack-reporter-custom-resource-prod-tmen2ec"
      OrgUid: !Ref OrgUid
      RoleArn: !GetAtt SFRole.Arn
      Alias: !Ref Alias
Outputs:
  SFRoleArn:
    Description: "ARN for the IAM Role used by Serverless Framework"
    Value: !GetAtt SFRole.Arn
Parameters:
  OrgUid:
    Description: Serverless Framework Org Uid
    Type: String
  Alias:
    Description: Serverless Framework Provider Alias
    Type: String
  RoleName:
    Description: Serverless Framework Role Name
    Type: String
```

</details>

<details>

<summary>Trust Relationship</summary>

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::486128539022:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "ServerlessFramework-7bf7ddef-e1bf-43eb-a111-4d43e0894ccb"
        }
      }
    }
  ]
}
```

</details>

5. The tutorial asks to create the file `createCustomer.js` which will basically create a new API endpoint handled by the new JS file and asks to modify the `serverless.yml` file to make it generate a **new DynamoDB table**, define an **environment variable**, the role that will be using the generated lambdas.

{{#tabs }}
{{#tab name="createCustomer.js" }}

```javascript
"use strict"
const AWS = require("aws-sdk")
module.exports.createCustomer = async (event) => {
  const body = JSON.parse(Buffer.from(event.body, "base64").toString())
  const dynamoDb = new AWS.DynamoDB.DocumentClient()
  const putParams = {
    TableName: process.env.DYNAMODB_CUSTOMER_TABLE,
    Item: {
      primary_key: body.name,
      email: body.email,
    },
  }
  await dynamoDb.put(putParams).promise()
  return {
    statusCode: 201,
  }
}
```

{{#endtab }}

{{#tab name="serverless.yml" }}

```yaml
# "org" ensures this Service is used with the correct Serverless Framework Access Key.
org: testing12342
# "app" enables Serverless Framework Dashboard features and sharing them with other Services.
app: tutorialapp
# "service" is the name of this project. This will also be added to your AWS resource names.
service: Tutorial

provider:
  name: aws
  runtime: nodejs20.x
  environment:
    DYNAMODB_CUSTOMER_TABLE: ${self:service}-customerTable-${sls:stage}
  iam:
    role:
      statements:
        - Effect: "Allow"
          Action:
            - "dynamodb:PutItem"
            - "dynamodb:Get*"
            - "dynamodb:Scan*"
            - "dynamodb:UpdateItem"
            - "dynamodb:DeleteItem"
          Resource: arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/${self:service}-customerTable-${sls:stage}

functions:
  hello:
    handler: handler.hello
    events:
      - httpApi:
          path: /
          method: get
  createCustomer:
    handler: createCustomer.createCustomer
    events:
      - httpApi:
          path: /
          method: post

resources:
  Resources:
    CustomerTable:
      Type: AWS::DynamoDB::Table
      Properties:
        AttributeDefinitions:
          - AttributeName: primary_key
            AttributeType: S
        BillingMode: PAY_PER_REQUEST
        KeySchema:
          - AttributeName: primary_key
            KeyType: HASH
        TableName: ${self:service}-customerTable-${sls:stage}
```

{{#endtab }}
{{#endtabs }}

6. Deploy it running **`serverless deploy`**
   1. The deployment will be performed via a CloudFormation Stack
   2. Note that the **lambdas are exposed via API gateway** and not via direct URLs
7. **Test it**
   1. The previous step will print the **URLs** where your API endpoints lambda functions have been deployed

## Security Review of Serverless.com

### **Misconfigured IAM Roles and Permissions**

Overly permissive IAM roles can grant unauthorized access to cloud resources, leading to data breaches or resource manipulation.

When no permissions are specified for the a Lambda function, a role with permissions only to generate logs will be created, like:

<details>

<summary>Minimum lambda permissions</summary>

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "logs:CreateLogStream",
        "logs:CreateLogGroup",
        "logs:TagResource"
      ],
      "Resource": [
        "arn:aws:logs:us-east-1:123456789012:log-group:/aws/lambda/jito-cranker-scripts-dev*:*"
      ],
      "Effect": "Allow"
    },
    {
      "Action": ["logs:PutLogEvents"],
      "Resource": [
        "arn:aws:logs:us-east-1:123456789012:log-group:/aws/lambda/jito-cranker-scripts-dev*:*:*"
      ],
      "Effect": "Allow"
    }
  ]
}
```

</details>

#### **Mitigation Strategies**

- **Principle of Least Privilege:** Assign only necessary permissions to each function.

  ```yaml
  provider:
    [...]
    iam:
      role:
        statements:
          - Effect: 'Allow'
            Action:
              - 'dynamodb:PutItem'
              - 'dynamodb:Get*'
              - 'dynamodb:Scan*'
              - 'dynamodb:UpdateItem'
              - 'dynamodb:DeleteItem'
            Resource: arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/${self:service}-customerTable-${sls:stage}
  ```

- **Use Separate Roles:** Differentiate roles based on function requirements.

---

### **Insecure Secrets and Configuration Management**

Storing sensitive information (e.g., API keys, database credentials) directly in **`serverless.yml`** or code can lead to exposure if repositories are compromised.

The **recommended** way to store environment variables in **`serverless.yml`** file from serverless.com (at the time of this writing) is to use the `ssm` or `s3` providers, which allows to get the **environment values from these sources at deployment time** and **configure** the **lambdas** environment variables with the **text clear of the values**!

> [!CAUTION]
> Therefore, anyone with permissions to read the lambdas configuration inside AWS will be able to **access all these environment variables in clear text!**

For example, the following example will use SSM to get an environment variable:

```yaml
provider:
  environment:
    DB_PASSWORD: ${ssm:/aws/reference/secretsmanager/my-db-password~true}
```

And even if this prevents hardcoding the environment variable value in the **`serverless.yml`** file, the value will be obtained at deployment time and will be **added in clear text inside the lambda environment variable**.

> [!TIP]
> The recommended way to store environment variables using serveless.com would be to **store it in a AWS secret** and just store the secret name in the environment variable and the **lambda code should gather it**.

#### **Mitigation Strategies**

- **Secrets Manager Integration:** Use services like **AWS Secrets Manager.**
- **Encrypted Variables:** Leverage Serverless Framework’s encryption features for sensitive data.
- **Access Controls:** Restrict access to secrets based on roles.

---

### **Vulnerable Code and Dependencies**

Outdated or insecure dependencies can introduce vulnerabilities, while improper input handling may lead to code injection attacks.

#### **Mitigation Strategies**

- **Dependency Management:** Regularly update dependencies and scan for vulnerabilities.

  ```yaml
  plugins:
    - serverless-webpack
    - serverless-plugin-snyk
  ```

- **Input Validation:** Implement strict validation and sanitization of all inputs.
- **Code Reviews:** Conduct thorough reviews to identify security flaws.
- **Static Analysis:** Use tools to detect vulnerabilities in the codebase.

---

### **Inadequate Logging and Monitoring**

Without proper logging and monitoring, malicious activities may go undetected, delaying incident response.

#### **Mitigation Strategies**

- **Centralized Logging:** Aggregate logs using services like **AWS CloudWatch** or **Datadog**.

  ```yaml
  plugins:
    - serverless-plugin-datadog
  ```

- **Enable Detailed Logging:** Capture essential information without exposing sensitive data.
- **Set Up Alerts:** Configure alerts for suspicious activities or anomalies.
- **Regular Monitoring:** Continuously monitor logs and metrics for potential security incidents.

---

### **Insecure API Gateway Configurations**

Open or improperly secured APIs can be exploited for unauthorized access, Denial of Service (DoS) attacks, or cross-site attacks.

#### **Mitigation Strategies**

- **Authentication and Authorization:** Implement robust mechanisms like OAuth, API keys, or JWT.

  ```yaml
  functions:
    hello:
      handler: handler.hello
      events:
        - http:
            path: hello
            method: get
            authorizer: aws_iam
  ```

- **Rate Limiting and Throttling:** Prevent abuse by limiting request rates.

  ```yaml
  provider:
    apiGateway:
      throttle:
        burstLimit: 200
        rateLimit: 100
  ```

- **Secure CORS Configuration:** Restrict allowed origins, methods, and headers.

  ```yaml
  functions:
    hello:
      handler: handler.hello
      events:
        - http:
            path: hello
            method: get
            cors:
              origin: https://yourdomain.com
              headers:
                - Content-Type
  ```

- **Use Web Application Firewalls (WAF):** Filter and monitor HTTP requests for malicious patterns.

---

### **Insufficient Function Isolation**

Shared resources and inadequate isolation can lead to privilege escalations or unintended interactions between functions.

#### **Mitigation Strategies**

- **Isolate Functions:** Assign distinct resources and IAM roles to ensure independent operation.
- **Resource Partitioning:** Use separate databases or storage buckets for different functions.
- **Use VPCs:** Deploy functions within Virtual Private Clouds for enhanced network isolation.

  ```yaml
  provider:
    vpc:
      securityGroupIds:
        - sg-xxxxxxxx
      subnetIds:
        - subnet-xxxxxx
  ```

- **Limit Function Permissions:** Ensure functions cannot access or interfere with each other’s resources unless explicitly required.

---

### **Inadequate Data Protection**

Unencrypted data at rest or in transit can be exposed, leading to data breaches or tampering.

#### **Mitigation Strategies**

- **Encrypt Data at Rest:** Utilize cloud service encryption features.

  ```yaml
  resources:
    Resources:
      MyDynamoDBTable:
        Type: AWS::DynamoDB::Table
        Properties:
          SSESpecification:
            SSEEnabled: true
  ```

- **Encrypt Data in Transit:** Use HTTPS/TLS for all data transmissions.
- **Secure API Communication:** Enforce encryption protocols and validate certificates.
- **Manage Encryption Keys Securely:** Use managed key services and rotate keys regularly.

---

### **Lack of Proper Error Handling**

Detailed error messages can leak sensitive information about the infrastructure or codebase, while unhandled exceptions may lead to application crashes.

#### **Mitigation Strategies**

- **Generic Error Messages:** Avoid exposing internal details in error responses.

  ```javascript
  javascriptCopy code// Example in Node.js
  exports.hello = async (event) => {
    try {
      // Function logic
    } catch (error) {
      console.error(error);
      return {
        statusCode: 500,
        body: JSON.stringify({ message: 'Internal Server Error' }),
      };
    }
  };
  ```

- **Centralized Error Handling:** Manage and sanitize errors consistently across all functions.
- **Monitor and Log Errors:** Track and analyze errors internally without exposing details to end-users.

---

### **Insecure Deployment Practices**

Exposed deployment configurations or unauthorized access to CI/CD pipelines can lead to malicious code deployments or misconfigurations.

#### **Mitigation Strategies**

- **Secure CI/CD Pipelines:** Implement strict access controls, multi-factor authentication (MFA), and regular audits.
- **Store Configuration Securely:** Keep deployment files free from hardcoded secrets and sensitive data.
- **Use Infrastructure as Code (IaC) Security Tools:** Employ tools like **Checkov** or **Terraform Sentinel** to enforce security policies.
- **Immutable Deployments:** Prevent unauthorized changes post-deployment by adopting immutable infrastructure practices.

---

### **Vulnerabilities in Plugins and Extensions**

Using unvetted or malicious third-party plugins can introduce vulnerabilities into your serverless applications.

#### **Mitigation Strategies**

- **Vet Plugins Thoroughly:** Assess the security of plugins before integration, favoring those from reputable sources.
- **Limit Plugin Usage:** Use only necessary plugins to minimize the attack surface.
- **Monitor Plugin Updates:** Keep plugins updated to benefit from security patches.
- **Isolate Plugin Environments:** Run plugins in isolated environments to contain potential compromises.

---

### **Exposure of Sensitive Endpoints**

Publicly accessible functions or unrestricted APIs can be exploited for unauthorized operations.

#### **Mitigation Strategies**

- **Restrict Function Access:** Use VPCs, security groups, and firewall rules to limit access to trusted sources.
- **Implement Robust Authentication:** Ensure all exposed endpoints require proper authentication and authorization.
- **Use API Gateways Securely:** Configure API Gateways to enforce security policies, including input validation and rate limiting.
- **Disable Unused Endpoints:** Regularly review and disable any endpoints that are no longer in use.

---

### **Excessive Permissions for Team Members and External Collaborators**

Granting excessive permissions to team members and external collaborators can lead to unauthorized access, data breaches, and misuse of resources. This risk is heightened in environments where multiple individuals have varying levels of access, increasing the attack surface and potential for insider threats.

#### **Mitigation Strategies**

- **Principle of Least Privilege:** Ensure that team members and collaborators have only the permissions necessary to perform their tasks.

---

### **Access Keys and License Keys Security**

**Access Keys** and **License Keys** are critical credentials used to authenticate and authorize interactions with the Serverless Framework CLI.

- **License Keys:** They are Unique identifiers required for authenticating access to Serverless Framework Version 4 which allows to login via CLI.
- **Access Keys:** Credentials that allow the Serverless Framework CLI to authenticate with the Serverless Framework Dashboard. When login with `serverless` cli an access key will be **generated and stored in the laptop**. You can also set it as an environment variable named `SERVERLESS_ACCESS_KEY`.

#### **Security Risks**

1. **Exposure Through Code Repositories:**
   - Hardcoding or accidentally committing Access Keys and License Keys to version control systems can lead to unauthorized access.
2. **Insecure Storage:**
   - Storing keys in plaintext within environment variables or configuration files without proper encryption increases the likelihood of leakage.
3. **Improper Distribution:**
   - Sharing keys through unsecured channels (e.g., email, chat) can result in interception by malicious actors.
4. **Lack of Rotation:**
   - Not regularly rotating keys extends the exposure period if keys are compromised.
5. **Excessive Permissions:**
   - Keys with broad permissions can be exploited to perform unauthorized actions across multiple resources.

{{#include ../banners/hacktricks-training.md}}



