Skip to content

Latest commit

 

History

History
 
 

03-input-validation

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

Module 3: Input validation on API Gateway

A quote from the OWASP website:

"The most common web application security weakness is the failure to properly validate input from the client or environment."

--- OWASP (The Open Web Application Security Project)

A great XKCD comic demonstrate this point well:

xkcd exploits_of_a_mom

You can configure API Gateway to perform basic validation of an API request before proceeding with the integration request. When the validation fails, API Gateway immediately fails the request, returns a 400 error response to the caller, and publishes the validation results in CloudWatch Logs. This reduces unnecessary calls to the backend. More importantly, it lets you focus on the validation efforts specific to your application.

For example, in our application, when defining an customization, we have to be sure that our new customization should have:

  • A name for the customization object
  • An url for the cape's image .
  • A type of socks for our unicorn specified by an id.
  • A specific id for the horn to use.
  • An id for the pair of glasses.
  • A type of cape by id.

This information should be in our request body to create a new customization that follows specific patterns. E.g. the imageUrl should be a valid URL, the IDs for socks and horns are numeric values.

By leveraging input validation on API Gateway, you can enforce required parameters and regex patterns each parameter must adhere to. This allows you to remove boilerplate validation logic from backend implementations and focus on actual business logic and deep validation.

Module 3 - Optional: attack your API with SQL injection!

If you haven't completed Module 6: WAF, your serverless API is currently vulnerable to SQL injection attacks. This optional module shows how you can perform the attack.

Click to expand for optional step instructions

If you look at our lambda function code right now, no input validation is being performed, and with the below line specified as part of our mysql client setting (under /src/app/dbUtils.js):

                multipleStatements: true

Note: As a best practice you should set the multipleStatements option to false in your code (the nodejs mysql client actually defaults it false). However, this is not disabled by default in all programming languages/libraries, so we enabled it in our starter code for you to see the easiness of this attack.

we can easily embed SQL statements in the body of the request to get executed. For example, in the body of the POST customizations/ API, try using the below:

If you have done module 1, use sample input here
{  
   "name":"Orange-themed unicorn",
   "imageUrl":"https://en.wikipedia.org/wiki/Orange_(fruit)#/media/File:Orange-Whole-%26-Split.jpg",
   "sock":"1",
   "horn":"2",
   "glasses":"3",
   "cape":"2); INSERT INTO Socks (NAME,PRICE) VALUES ('Bad color', 10000.00"
}
If you have not done module 1, use sample input here
{  
   "name":"Orange-themed unicorn",
   "imageUrl":"https://en.wikipedia.org/wiki/Orange_(fruit)#/media/File:Orange-Whole-%26-Split.jpg",
   "sock":"1",
   "horn":"2",
   "glasses":"3",
   "cape":"2); INSERT INTO Socks (NAME,PRICE) VALUES ('Bad color', 10000.00",
   "company":"1"
}

Send the request using Postman. If the request succeeds, you have now just performed a SQL injection attack!

If you look at the SQL injection statement we just performed, it's adding a bad value into the Socks table. We can verify that took effect by running the GET /socks API:

Module 3A: Create a model for your Customizations

In API Gateway, a model defines the data structure of a payload, using the JSON schema draft 4.

When we define our model, we can ensure that the parameters we are receiving are in the format we are expecting. Furthermore, you can check them against regex expressions. A good tool to test if your regex is correct is regexr.com.

For our POST /customizations API, we are going to use the following model:

{
  "title": "Customizations",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "required": [
    "imageUrl",
    "sock",
    "horn",
    "glasses",
    "cape",
    "name"
  ],
  "properties": {
    "imageUrl": {
      "type": "string",
      "title": "The Imageurl Schema",
      "pattern": "^https?:\/\/[-a-zA-Z0-9@:%_+.~#?&//=]+$"
    },
    "name": {
      "type": "string",
      "title": "The name Schema",
      "pattern": "^[a-zA-Z0-9- ]+$"
    },
    "sock": {
      "type": "string",
      "title": "The Sock Schema",
      "pattern": "^[0-9]*$"
    },
    "horn": {
      "type": "string",
      "title": "The Horn Schema",
      "pattern": "^[0-9]*$"
    },
    "glasses": {
      "type": "string",
      "title": "The Glasses Schema",
      "pattern": "^[0-9]*$"
    },
    "cape": {
      "type": "string",
      "title": "The Cape Schema",
      "pattern": "^[0-9]*$"
    }
  }
}

Now, follow these steps:

  1. Go to API Gateway console.

  2. Click on the API CustomizeUnicorns

  3. Click on Models

  4. Click on Create and create a model with the following values:

    • Model name: CustomizationPost
    • Content type: application/json
  5. In the model schema, use the one provided before (the json before this section).

  6. Once everything is filled, click on Create model.

    Create model

Once we have created our model, we need to apply it to our customizations/post method.

  1. Within the API Gateway Console, click on CustomizeUnicorns, Resources

  2. Click under /customizations --> POST method

    Customizations

  3. Click on Method Request

  4. Under Request Validator, click on the pencil to edit it. Select Validate Body. Then, click on the tick to confirm the change.

  5. Under Request Body, click on Add model with the following values:

    • Content type: application/json
    • Model name: CustomizationPost
  6. Click to the tick to confirm.

    Method Execution

    On step number 2 you might have noticed that we can also validate query parameters and request headers in addition to request body. This is really useful when our application uses both at the same time and we want to have complex validations. If you want to find more information, here is our documentation about this.

    1. Now it's time to deploy and test! Go to the Actions menu and click on Deploy API. Select dev as the Deployment stage and confirm by clicking Deploy.

Module 3B: Test your Validation

Use postman, you can try making requests to the POST /customizations API using invalid parameters and see the input validation kick in (if you get an unauthorize error message, could be caused by the expiration time of the Authentication token. You can easily refresh rthis token following these steps from module 01:

Wrong parameters = Invalid request:

Here are some example request bodies that fail:

  • Missing fields:

     {  
        "name":"Cherry-themed unicorn",
        "imageUrl":"https://en.wikipedia.org/wiki/Cherry#/media/File:Cherry_Stella444.jpg",
        "glasses": "3",
        "cape": "4"
     }
  • The imageUrl not a valid URL:

     {  
        "name":"Cherry-themed unicorn",
        "imageUrl":"htt://en.wikipedia.org/wiki/Cherry#/media/File:Cherry_Stella444.jpg",
        "sock": "1" ,
        "horn": "2" ,
        "glasses": "3",
        "cape": "4"
     }
  • The cape parameter not a number (SQL injection attempt)

     {  
        "name":"Orange-themed unicorn",
        "imageUrl":"https://en.wikipedia.org/wiki/Orange_(fruit)#/media/File:Orange-Whole-%26-Split.jpg",
        "sock": "1",
        "horn": "2",
        "glasses": "3",
        "cape":"2); INSERT INTO Socks (NAME,PRICE) VALUES ('Bad color', 10000.00"
     }

You should get a 400 Bad Request response:

{"message": "Invalid request body"}

Correct parameters

Testing the POST /customizations API with right parameters:

If you have done module 1, use sample input here
{  
   "name":"Cherry-themed unicorn",
   "imageUrl":"https://en.wikipedia.org/wiki/Cherry#/media/File:Cherry_Stella444.jpg",
   "sock": "1",
   "horn": "2",
   "glasses": "3",
   "cape": "4"
}
If you have not done module 1, use sample input here
{  
   "name":"Cherry-themed unicorn",
   "imageUrl":"https://en.wikipedia.org/wiki/Cherry#/media/File:Cherry_Stella444.jpg",
   "sock": "1",
   "horn": "2",
   "glasses": "3",
   "cape": "4",
   "company" : "1"
}

The result should be:

{"customUnicornId":<the-id-of-the-customization>}

Additional input validation options

As you have now seen, API Gateway input validation gives you basic features such as type checks and regex matching. In a production application, this is often not enough and you may have additional constraints on the API input.

To gain further protection, you should consider using the below in addition to the input validation features from API Gateway:

  • Add an AWS WAF ACL to your API Gateway - check out Module 6
  • Add further input validation logic in your lambda function code itself

Extra credit

There is, at least, one more method that needs to be validated. Build your own json schema for that method and apply the same steps mentioned before and you should be able to validate these methods as well!

Hint: In case you need some help, here is the model to be used:
{
  "title": "PartnerPOST",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "required": [
    "name"
  ],
  "properties": {
    "name": {
      "type": "string",
      "title": "Partner Schema",
      "pattern": "^[a-zA-Z0-9- ]+$"
    }
  }
}

Next step

You have now added basic input validation to your API and further reduced the risk of attackers using bad inputs to sabotage your API!

Return to the workshop landing page to pick another module.