Consejos de seguridad de Express.js: cómo puede guardar y proteger su aplicación

Siga 7 pasos para asegurarse de que su aplicación sea invencible

¿Tu teléfono está bloqueado? ¿Tiene un código pin, contraseña, huella digital o FaceID? Estoy 99 por ciento seguro de que sí. Y está claro por qué: te preocupas por tu seguridad. Hoy en día, mantener tu teléfono protegido es tan importante como cepillarte los dientes por la mañana.

Para los desarrolladores de software diligentes y conscientes, mantener su aplicación segura es igualmente importante para proteger sus teléfonos. Si es un desarrollador y decide descuidarlo, reconsidere su enfoque. Si usted es propietario de un proyecto y su equipo de desarrollo dice que la seguridad de los datos puede esperar, reconsidere su equipo.

En este artículo, quiero hablar sobre cómo asegurarme de que su proyecto Express.js sea seguro e invencible a ataques maliciosos.

Hay 7 medidas simples y no muy simples para tomar con el propósito de la seguridad de los datos:

  1. Use versiones confiables de Express.js
  2. Asegure la conexión y los datos
  3. Proteja sus cookies
  4. Asegure sus dependencias
  5. Valida la entrada de tus usuarios
  6. Proteja su sistema contra la fuerza bruta
  7. Controlar el acceso de los usuarios

Echemos un vistazo más de cerca a cada uno.

1. Utilice versiones confiables de Express.js

Las versiones obsoletas o desactualizadas de Express.js no son válidas. La segunda y tercera versión de Express ya no son compatibles. En estos, los problemas de seguridad o rendimiento ya no se solucionan.

Como desarrollador, es absolutamente necesario migrar a Express 4. ¡Esta versión es una revolución! Es bastante diferente en términos del sistema de enrutamiento, middleware y otros aspectos menores.

2. Asegure la conexión y los datos

Para proteger los encabezados HTTP, puede hacer uso de Helmet.js, un útil módulo de Node.js. Es una colección de 13 funciones de middleware para configurar encabezados de respuesta HTTP. En particular, hay funciones para configurar la Política de seguridad del contenido, manejar la Transparencia de certificados, evitar el secuestro de clics, deshabilitar el almacenamiento en caché del lado del cliente o agregar algunas pequeñas protecciones XSS.

npm install helmet --save

Incluso si no desea utilizar todas las funciones de Helmet, lo mínimo absoluto que debe hacer es deshabilitar el encabezado X-Powered-By:

app.disable('x-powered-by')

Este encabezado se puede utilizar para detectar que la aplicación funciona con Express, lo que permite a los piratas informáticos realizar un ataque preciso. Sin duda, el encabezado X-Powered-By no es la única forma de identificar una aplicación de ejecución rápida, pero es probablemente la más común y simple.

Para proteger su sistema de ataques de contaminación de parámetros HTTP, puede utilizar HPP. Este middleware deja de lado parámetros como req.query y req.body y selecciona el último valor de parámetro. El comando de instalación tiene el siguiente aspecto:

npm install hpp --save 

Para cifrar los datos que se envían desde el cliente al servidor, utilice Transport Layer Security (TLS). TLS es un protocolo criptográfico para proteger la red informática, el descendiente del cifrado Secure Socket Layer (SSL). TLS se puede gestionar con Nginx, un servidor HTTP gratuito pero eficaz, y Let's Encrypt, un certificado TLS gratuito.

3. Proteja sus cookies

En Express.js 4, hay dos módulos de sesión de cookies:

  • express-session (en Express.js 3, era express.session)
  • cookie-session (en Express.js 3, era express.cookieSession)

El módulo de sesión rápida almacena el ID de la sesión en la cookie y los datos de la sesión en el servidor. La sesión de cookies almacena todos los datos de la sesión en la cookie.

En general, la sesión de cookies es más eficiente. Sin embargo, si los datos de la sesión que necesita almacenar son complejos y es probable que excedan los 4096 bytes por cookie, use express-session. Otra razón para usar express-session es cuando necesita mantener los datos de las cookies invisibles para el cliente.

Además, debe configurar las opciones de seguridad de las cookies, a saber:

  • seguro
  • httpOnly
  • dominio
  • camino
  • expira

Si "seguro" se establece en "verdadero", el navegador enviará cookies solo a través de HTTPS. Si "httpOnly" se establece en "true", la cookie no se enviará a través de JS del cliente sino a través de HTTP (S). El valor de "dominio" indica el dominio de la cookie. Si el dominio de la cookie coincide con el dominio del servidor, se utiliza "ruta" para indicar la ruta de la cookie. Si la ruta de la cookie coincide con la ruta de la solicitud, la cookie se enviará en la solicitud. Finalmente, como su propio nombre sugiere, el valor de "expires" representa el momento en que expirarán las cookies.

Otra recomendación importante es no utilizar el nombre de cookie de sesión predeterminado. Puede permitir a los piratas informáticos detectar el servidor y ejecutar un ataque dirigido. En su lugar, utilice nombres de cookies genéricos.

4. Asegure sus dependencias

Sin duda, npm es una poderosa herramienta de desarrollo web. Sin embargo, para garantizar el más alto nivel de seguridad, considere usar solo la sexta versión: npm @ 6. Los más antiguos pueden contener algunas vulnerabilidades de seguridad de dependencia graves, que pondrán en peligro toda su aplicación. Además, para analizar el árbol de dependencias, use el siguiente comando:

npm audit

npm audit puede ayudar a solucionar problemas reales en su proyecto. Verifica todas sus dependencias en dependencias, devDependencies, bundledDependencies y optionalDependencies, pero no sus peerDependencies. Aquí puede leer acerca de todas las vulnerabilidades actuales en cualquier paquete npm.

Securing dependencies

Otra herramienta para garantizar la seguridad de la dependencia es Snyk. Snyk ejecuta la verificación de la aplicación para identificar si contiene alguna vulnerabilidad listada en la base de datos de código abierto de Snyk. Para realizar la verificación, ejecute tres sencillos pasos.

Paso 1. Instale Snyk

npm install -g snyk cd your-app

Paso 2. Ejecuta una prueba

snyk test

Step 3. Learn how to fix the issue

snyk wizard

Wizard is a Snyk method, which explains the nature of the dependency vulnerability and offers ways of fixing it.

5. Validate the input of your users

Controlling user input is an extremely important part for server-side development. This is a no less important problem than unauthorized requests, which will be described in the seventh part of this article.

First of all, wrong user input can break your server when some values are undefined and you do not have error handling for a specific endpoint. However, different ORM systems can have unpredictable behavior when you try to set undefined, null, or other data types in the database.

For example, destroyAll method in Loopback.js ORM (Node.js framework) can destroy all data in a table of the database: when it does not match any records it deletes everything as described here. Imagine that you can lose all data in a production table just because you have ignored input validation.

Use body/object validation for intermediate inspections

To start with, you can use body/object validation for intermediate inspections. For example, we use ajv validator which is the fastest JSON Schema validator for Node.js.

const Ajv = require('ajv'); const ajv = new Ajv({allErrors: true}); const speaker = { 'type': 'object', 'required': [ 'id', 'name' ], 'properties': { 'id': { 'type': 'integer', }, 'name': { 'type': 'string', }, }, }; const conversation = { type: 'object', required: [ 'duration', 'monologues' ], properties: { duration: { type: 'integer', }, monologues: { type: 'array', items: monolog, }, }, }; const body = { type: 'object', required: [ 'speakers', 'conversations' ], properties: { speakers: { type: 'array', items: speaker, }, conversations: { type: 'array', items: conversation, }, }, }; const validate = ajv.compile(body); const isValidTranscriptBody = transcriptBody => { const isValid = validate(transcriptBody); if (!isValid) { console.error(validate.errors); } return isValid; };

Handle errors

Now, imagine that you forgot to check a certain object and you do some operations with the undefined property. Or you use a certain library and you get an error. It can break your instance, and the server will crash. Then, the attacker can ping a specific endpoint where there is this vulnerability and can stop your server for a long time.

The simplest way to do an error handling is to use try-catch construction:

try { const data = body; if (data.length === 0) throw new Error('Client Error'); const beacons = await this.beaconLogService.filterBeacon(data); if (beacons.length > 0) { const max = beacons.reduce((prev, current) => (prev.rssi > current.rssi) ? prev : current); await this.beaconLogService.save({ ...max, userId: headers['x-uuid'] }); return { data: { status: 'Saved', position: max }, }; } return { data: { status: 'Not valid object, }, }; } catch(err) { this.logger.error(err.message, err.stack); throw new HttpException('Server Error', HttpStatus.INTERNAL_SERVER_ERROR); }

Feel free to use a new Error(‘message’) constructor for error handling or even extend this class for your own purpose!

Use JOI

The main lesson here is that you should always validate user input so you don't fall victim to man-in-the-middle attacks. Another way to do it is with the help of @hapi/joi – a part of the hapi ecosystem and a powerful JS data validation library.

Pay attention here that the module joi has been deprecated. For this reason, the following command is a no go:

npm install joi

Instead, use this one:

npm install @hapi/joi

Use express-validator

One more way to validate user input is to use express-validator – a set of express.js middlewares, which comprises validator.js and function sanitizer. To install it, run the following command:

npm install --save express-validator 

Sanitize user input

Also, an important measure to take is to sanitize user input to protect the system from a MongoDB operator injection. For this, you should install and use express-mongo-sanitize:

npm install express-mongo-sanitize

Protect your app against CSRF

Besides, you should protect your app against cross-site request forgery (CSRF). CSRF is when unauthorized commands are sent from a trusted user. You can do this with the help of csurf. Prior to that, you need to make sure that session middleware for cookies is configured as described earlier in this article. To install this Node.js module, run the command:

npm install csurf 

6. Protect your system against brute force

A brute force attack is the simplest and most common way to get access to a website or a server. The hacker (in most cases automatically, rarely manually) tries various usernames and passwords repeatedly to break into the system.

These attacks can be prevented with the help of rate-limiter-flexible package. This package is fast, flexible, and suitable for any Node framework.

To install, run the following command:

npm i --save rate-limiter-flexible yarn add rate-limiter-flexible

This method has a simpler but more primitive alternative: express-rate-limit. The only thing it does is limiting repeated requests to public APIs or to password reset.

npm install --save express-rate-limit

7. Control user access

Among the authentication methods, there are tokens, Auth0, and JTW. Let’s focus on the third one! JTW (JSON Web Tokens) are used to transfer authentication data in client-server applications. Tokens are created by the server, signed with a secret key, and transferred to a client. Then, the client uses these tokens to confirm identity.

Express-jwt-permissions is a tool used together with express-jwt to check permissions of a certain token. These permissions are an array of strings inside the token:

"permissions": [   "status",   "user:read",   "user:write" ]

To install the tool, run the following command:

npm install express-jwt-permissions --save

To Wrap Up

Here, I have listed the essential Express.js security best practices and some tools that can be used along the way.

Just to review:

Securing dependencies

I strongly recommend that you make sure that your application is resistant to malicious attacks. Otherwise, your business and your users may suffer significant losses.

Do you have an idea for Express.js project?

My company KeenEthics is experienced in express js development. In case you need a free estimate of a similar project, feel free to get in touch.

If you have enjoyed the article, you should definitely continue with a piece on data safety in outsourcing to Ukraine: KeenEthics Team on Guard: Your Data is Safe in Ukraine. The original article posted on KeenEthics blog can be found here: express js security tips.

P.S.

A huge shout-out to Volodia Andrushchak, Full-Stack Software Developer @KeenEthics for helping me with the article.

The original article posted on KeenEthics blog can be found here: Express.js Security Tips: Save Your App!