2.0.0 • Published 2 years ago

hpp-prevent v2.0.0

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

HPP-PREVENT

codecov Generic badge Generic badge Generic badge

Express middleware for prevent http parameter pollution

hpp-prevent is a middleware for express to prevent hpp (http param pollution) attack

What is, and how works, a hpp attack ?

hpp is a type of attack where an external attacker adds parameters with the same name to a given endpoint. This, depending on the platform used, can generate unexpected behavior of the systems. Is the case of Express, more than one parameter with the same name will be added in an array. Or worse, this can override some valid parameters.

hpp-atack example

endpoint: https://your-api.com/name=value1&lastname=value2 The atacker can add more parameters with the same name to the enpoint url, this way.

endpoint: https://your-api.com/name=value1&lastname=value2&name=value3&lastname=value4

And then, in your backend you will get this output when you access the request.query object

{
name: ['value1','value3'],
lastname: ['value2','value4']
}

When this was the object you expected

{
name: 'value1',
lastname: 'value2'
}

This attack is normally used as a gateway to more sophisticated attacks, however, its destructive capacity should not be underestimated.

And how hpp-prevent solve this problem ?

hpp-prevent lets you configure the behavior of Express, you can choose to take the last or the first occurrence of some parameter when some parameter is passed more than one time. This is important because, sometimes you have to match the order of Express and other firewall systems, if this order of validation mismatch, your validation will be bypassed.

Using this middleware, this polluted endpoint: https://your-api.com/name=value1&lastname=value2&name=value3&lastname=value4 will result in a safe request.query object, like this.

{
name: 'value1',
lastname: 'value2'
}

***Click here for a more detailed explanation about http parameter pollution (By Owasp)***

How to use

To use hpp-prevent is pretty easy.

First import the library

const hppPrevent = require('hpp-prevent')

Second, configure the library.

To do so, you need to call the config method, that function accept these parameters:

  • takeLastOcurrences: Boolean that indicates what is the element that will be picked when more than one parameter with the same name is found, true indicates that the last element will be picked, false indicates that the first element will be picked. For default is true

  • blacklist: This is a list of terms that you want to explicitly block in your query parameters, once a term is put in this list, the parameter and value will be stripped off from parameters if a key or a value match the term in the blacklist. By default, this list comes filled with _proto_ and constructor words that are usually used to perform prototype pollution attacks.

  • whitelist: May want to have some parameter having multiple values, in this case you can put the parameters that you expect to have more than one value in this list. By default, is an empty list

  • returnBadRequestReponse: This variable controls if a bad request response should be returned if any of the parameters of the query object match to a term in the blacklist. If this variable is set to true and any term is found in blacklist then a bad Request response with status 400 will be returned, otherwise, if any of the parameters is found in black list and this variable is false then that parameter will be stripped off from the query object and no response will be returned, the processes of the request will continue as normal. By default, is set to false

  • customInvalidParamMessage: This is a string that will be returned in case of returnBadRequestReponse variable is set to true and any of the terms in query object is found in blacklist

  • canIgnoreBodyParse: This controls if you want or not to parse the top level body properties in search of prototypes patterns

  • deepSearch: This controls if you want to perform a deep search in request body and query objects, this perform a full scanning of query and body objects. but TAKE CARE. If the payload received is too large this can lead to a system degradation. I sugest to limit the maximum payload size of the request ( this is another best practice security). This way you will be safe to parse the full body of the request.

const hppPrevent = require('hpp-prevent');

hppPrevent.config({
  takeLastOcurrences: false,//this tell to take the first occurrence of any duplicated param
  blackList: ['select'],
  returnBadRequestReponse: true,
  customInvalidParamMessage: 'Invalid params detected, please review the request',
  deepSearch: true
})

Third, set the express app to use the middleware

app.use(hppPrevent.hppPrevent)

How to use as a endpoint level middleware

If you don't want to apply the middleware to all your endpoints, you can use it only on certain endpoints, this way

app.get('/your-endpoint',httpPrevent.hppPrevent,(request,response)=>{
  /// This way the middleware will be applied only on this endpoint
})

How to use the exposed parseParams method

In case you want to do the validation only in specific cases inside your endpoint, you can use the parseParams method to validate only the data you want and in the situation you want. This method will additionally remove any and all top-level properties from the request.body object that contain any reference to the javascript prototype object access terms to help prevent another type of attack, the Prototype Pollution Attack.

parseRequestBody receives theses arguments: bodyParams : Object with request body , the request.body object returns: Return a dto like

{
    sanitizedParams, // body parameters sanitized
    forbiddenParametersFound  // forbidden properties found in body object and removed from the sanitized parameters
}

parseParams method receives theses arguments:

objectParams: Object with query parameters, the request.query object

isLastParams : Boolean value to set the parameter's order, if false, take the first

occurance, otherwise take the last occurance

forbiddenTerms: List with the terms that you want to explicity block from query parameters

expectedParamsToBeArray: List with the params that you expect to be array in the query parameters

returns: Return a dto like

{
    sanitizedParams, // sanitized parameters
     forbiddenParametersFound  // forbidden properties found in object and removed from the sanitized parameters
}

How to use the exposed getCurrentConfig and resetConfig methods

If you, for some reason, need to retrive the current lib configuration you can do this calling the getCurrentConfig method. This method will return to you a dto like this

{

      isLastParams: true,
      forbiddenTerms: [ '__proto__', 'constructor' ],
      expectedParamsToBeArray: [],
      isToReturn400Reponse: false,
      invalidParamMessage: undefined,
      ignoreBodyParse: true,
      deepSearch: true

}

And, if you want to reset lib configuration to the original state, to aply different behavior to another endpoint for example, you can do this calling the method resetConfig method, this way:

hppPrevent.resetConfig()

🔴VERY IMPORTANT!!!!!🔴

You must parse the request query and body before apply this middleware. To do so, you can use the default express json parser, Express.json() and express.urlencoded({ extended: true}). This way:

... rest of your code

app.use(Express.json()); //This has to be called before middleware
app.use(express.urlencoded({ extended: true}))  // this also come before the middleware
app.use(hppPrevent.hppPrevent);

... rest of your code

Performance

To test the performance of the middleware I created a small api and ran a simple load test on top of two situations, a POST with body + query-parameters and a GET with query parameters. The result was very good. For a 300 concurrent request, this was te result

Image

As you can see, with deepSearch enabled, the request time is between 0.5 and 0.6 ms, that is very good for the first version of package :D

That 's all !!

Hope this package help you to make your api more secure 😀😀

CHANGE LOG ☑️Version 2.0.0 ☑️

Starting at version 2.0.0, to use the middleware it is necessary to call it in the form of a function. This is due to the optimization made so that it is possible to pass custom parameters to each endpoint, without the need to reset the lib configuration for each endpoint. This is the new way to apply the middleware

const express = require("express");
const httpPrevent = require("../hpp-prevent/index");
const app = express();
app.use(express.json());

❌The config method is deprecated and it is no needed anymore ❌
/*
httpPrevent.config({
  takeLastOcurrences: true,
  deepSearch: true,
  whiteList: ["friends", "tags"],
});
*/

✅ Now you pass the config directly to the middleware when using as a global middleware for all routers  ✅

app.use(httpPrevent.hppPrevent({
  takeLastOcurrences: true,
  deepSearch: true,
  whiteList: ["friends", "tags"],
}));

❌ When using the middleware as an endpoint middleware, now you can pass an custom config for that endpoint especifically without the need of reconfigure the hole lib to apply an different behavior for that endpoint❌

httpPrevent.config({
  takeLastOcurrences: true,
  deepSearch: true,
  whiteList: ["friends", "tags"],
});

app.post("/", httpPrevent.hppPrevent, (request, response) => {
  return response.send(request.body);
});

✅The new way is much more cleaner and easy to use ✅

app.post("/", httpPrevent.hppPrevent({
  takeLastOcurrences: true,
  deepSearch: true,
  whiteList: ["friends", "tags"],
}), (request, response) => {
  return response.send(request.body);
});

License

buils

About the author

Dev, security enthusiast, Gamer :D

LinkedIn: www.linkedin.com/in/ronaldo-mp

Github: https://github.com/R9n