NEW FEATURE
Cobalt PtaaS + DAST combines manual pentests and automated scanning for comprehensive applications security.
NEW FEATURE
Cobalt PtaaS + DAST combines manual pentests and automated scanning for comprehensive applications security.

Secure Software Best Practices: Validate User Input

Protect your systems from bad user input. In this article, we share best practices to validate user input, securely.

To create secure software, you need code that checks user input. Without such checks, you could see security vulnerabilities such as:

In this article, we explore an attack scenario. We also show how you can minimize your risks. Read further to learn best practices to safely validate user input. Developers who write secure code use Input Validation. Black hat hackers frequently attack websites with questionable input.

Attack Scenario

An attacker might think: “I can earn free money. All I need to do is find a website that allows me to buy a negative quantity of products.”

Review this code sample from the OWASP Juice Shop. It’s a perfect example of how an innocent mistake can cause huge business losses:

 async function quantityCheck (req: Request, res: Response, next: NextFunction, id: number, quantity: number) {
const product = await QuantityModel.findOne({ where: { ProductId: id } })
if (!product) {
throw new Error('No such product found!')
}

if (!product.limitPerUser || (product.limitPerUser && product.limitPerUser >= quantity) || security.isDeluxe(req)) {
if (product.quantity >= quantity) { // enough in stock?
next()

} else {
res.status(400).json({ error: res.__('We are out of stock! Sorry for the inconvenience.') })
}
} else {
res.status(400).json({ error: res.__('You can order only up to items of this product.', { quantity: product.limitPerUser.toString() }) })
}
}

 

Analysis

The OWASP Juice Shop frontend initiates an API Post request to add items to the user’s basket. The middleware function that is mounted on that API route handler (or API path) calls the quantityCheck() function.

The quantityCheck() function validates the quantity in the request payload, as it checks:

  • If the product is available (const product = await QuantityModel.findOne({ where: { ProductId: id } }))
  • If the quantity exceeds the quantity available (product.quantity >= quantity)
  • If the quantity exceeds the limit per user ((product.limitPerUser && product.limitPerUser >= quantity)) except for deluxe users (security.isDeluxe(req))

However, it does not check if the quantity is a positive whole number.

This small mistake is costly. Users can freely input negative values for quantity. In that case:

  • Assume an attacker adds -100 units to their basket
    • The attacker then checks out of their shopping cart, with the wallet payment option
  • The Juice Shop adds 100 times the value of the item to the wallet

Impact

Without such checks, this error could lead to substantial business losses.

Prevention

In the following code sample, we include code that:

  • Throws an error for invalid quantities (quantity <= 0)
  • Runs a boolean check for decimal integers (Number.isInteger(quantity, 10))
  • Returns an HTTP 400 error if the noted checks fail
async function quantityCheck (req: Request, res: Response, next: NextFunction, id: number, quantity: number) {
const product = await QuantityModel.findOne({ where: { ProductId: id } })
if (!product) {
throw new Error('No such product found!')
}

// Check if the quantity is a positive decimal integer value, if not send a `400` error response
if(!Number.isInteger(quantity, 10) || quantity <= 0) {
res.status(400).json({ error: res.__('Invalid Quantity of items.') })
}

if (!product.limitPerUser || (product.limitPerUser && product.limitPerUser >= quantity) || security.isDeluxe(req)) {
if (product.quantity >= quantity) { // enough in stock?
next()
} else {
res.status(400).json({ error: res.__('We are out of stock! Sorry for the inconvenience.') })
}
} else {
res.status(400).json({ error: res.__('You can order only up to items of this product.', { quantity: product.limitPerUser.toString() }) })
}
}

 

Best Practices

  • Assume that any input can be an attack.

  • Validate all user input before processing it.

  • Check if the user input matches the right format.

    • For example, the application should limit date of birth entries to ISO 8601 format (DD-MM-YYYY).
  • Make sure user input fits the given business context. For example:

    • Limit entries in quantity fields to positive whole numbers. Prevent negative or fractional entries.
    • Set sensible minimum and maximum values for the quantity. Small quantities may not be financially viable for a given transaction.
  • Include an allow list of valid inputs. Attackers can often bypass the deny lists. For example, you could use the following to validate a US zip code:

    // define the allow list regex for US Zip code
    const validZipPattern = /^\d{5}(-\d{4})?$/;
    // check if the user input matches our defined allow list regex
    if (!validZipPattern.test(userInput) {
    throw new Error("InvalidZipCode");
    }
  • Log all input validation failures. The information you get can help you detect intrusions, and identify attackers who try to send invalid inputs. For this code sample, you can add the following logging statement: logger.warn("Invalid zip code provided."):

    // define the allow list regex for US Zip code
    const validZipPattern = /^\d{5}(-\d{4})?$/;
    // check if the user input matches our defined allow list regex
    if (!validZipPattern.test(userInput) {
    // log the input validation failure
    logger.warn("Invalid zip code provided.");
    throw new Error("InvalidZipCode");
    }

References

Back to Blog
About payloadartist
payloadartist is an anonymous contributor, who has helped secure organizations like Google. He's currently building security @CakeDeFi and writes about hacking, Web3 and cybersecurity. More By payloadartist
Mass Assignment & APIs - Exploitation in the Wild
APIs have become an integral part of many applications, with REST APIs being a popular choice for implementation. However, this popularity has led to security risks, with OWASP API Top 10 identifying vulnerabilities commonly found in APIs, including mass assignment. Harsh Bothra writes about this in his latest blog.
Blog
May 1, 2023
Source Code Review
Are you checking your new products for vulnerabilities in all capacities? Ninad Mathpati shares what you need to be doing during your Source Code Review and what attackers look for.
Blog
Nov 9, 2022
Azure AD: Pentesting Fundamentals
Core member Orhan Yildirim walks us through how to use Azure AD when pentesting.
Blog
May 23, 2022