Съвети за сигурност на Express.js: Как можете да запазите и защитите приложението си

Направете 7 стъпки, за да сте сигурни, че приложението ви е непобедимо

Заключен ли е телефонът ви? Имате ли ПИН-код, парола, пръстов отпечатък или FaceID? Сигурен съм 99 процента, че го правите. И е ясно защо - вие се грижите за вашата безопасност. В днешно време защитата на телефона ви е толкова важна, колкото и миенето на зъбите сутрин.

За усърдните и внимателни разработчици на софтуер защитата на приложението им е еднакво важна за защитата на телефоните им. Ако сте разработчик и решите да го пренебрегнете - моля, преразгледайте подхода си. Ако сте собственик на проект и вашият екип за разработка казва, че безопасността на данните може да изчака, моля, преразгледайте екипа си.

В тази статия искам да говоря за това как да се уверите, че вашият проект Express.js е безопасен и непобедим за злонамерени атаки.

Има 7 прости и не много прости мерки, които да предприемете за целите на сигурността на данните:

  1. Използвайте надеждни версии на Express.js
  2. Защитете връзката и данните
  3. Защитете своите бисквитки
  4. Защитете вашите зависимости
  5. Проверете въведеното от вашите потребители
  6. Защитете вашата система срещу груба сила
  7. Контролирайте потребителския достъп

Нека разгледаме по-отблизо всеки.

1. Използвайте надеждни версии на Express.js

Оттеглените или остарелите версии на Express.js не се използват. Втора и трета версия на Express вече не се поддържат. В тях проблемите с безопасността или производителността вече не са решени.

Като разработчик абсолютно трябва да мигрирате към Express 4. Тази версия е революция! Съвсем различно е по отношение на системата за маршрутизация, междинния софтуер и други незначителни аспекти.

2. Защитете връзката и данните

За да осигурите HTTP заглавия, можете да използвате Helmet.js - полезен модул Node.js. Това е колекция от 13 функции на междинен софтуер за задаване на заглавки на HTTP отговор. По-специално има функции за задаване на Политика за защита на съдържанието, работа с прозрачността на сертификата, предотвратяване на щракване, деактивиране на кеширането от страна на клиента или добавяне на някои малки XSS защити.

npm install helmet --save

Дори и да не искате да използвате всички функции на Helmet, абсолютният минимум, който трябва да направите, е да деактивирате X-Powered-By заглавката:

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

Този хедър може да се използва за откриване, че приложението се захранва от Express, което позволява на хакерите да извършват прецизна атака. Със сигурност заглавката X-Powered-By не е единственият начин да се идентифицира Express-run приложение, но е може би най-често срещаният и прост.

За да защитите системата си от атаки на замърсяване с HTTP параметри, можете да използвате HPP. Този междинен софтуер оставя настрана параметри като req.query и req.body и вместо това избира последната стойност на параметъра. Инсталационната команда изглежда по следния начин:

npm install hpp --save 

За да шифровате данни, които се изпращат от клиента към сървъра, използвайте Transport Layer Security (TLS). TLS е криптографски протокол за защита на компютърната мрежа, потомък на Secure Socket Layer (SSL) криптиране. TLS може да се обработва с Nginx - безплатен, но ефективен HTTP сървър - и Let's Encrypt - безплатен TLS сертификат.

3. Защитете своите бисквитки

В Express.js 4 има два модула за бисквитки:

  • експрес-сесия (в Express.js 3 това беше express.session)
  • cookie-session (в Express.js 3, беше express.cookieSession)

Модулът Express-session съхранява идентификатора на сесията в бисквитката и данните за сесията на сървъра. Сесията с бисквитки съхранява всички данни за сесията в бисквитката.

Като цяло бисквитката е по-ефективна. И все пак, ако данните за сесията, които трябва да съхранявате, са сложни и вероятно надвишават 4096 байта на бисквитка, използвайте express-session. Друга причина да използвате експрес-сесия е, когато трябва да запазите данните за бисквитките невидими за клиента.

Освен това трябва да зададете опции за защита на бисквитките, а именно:

  • сигурен
  • httpOnly
  • домейн
  • път
  • изтича

Ако „защитен“ е зададен на „вярно“, браузърът ще изпраща бисквитки само чрез HTTPS. Ако „httpOnly“ е зададено на „true“, бисквитката ще бъде изпратена не чрез JS на клиента, а чрез HTTP (S). Стойността на „домейн“ показва домейна на бисквитката. Ако домейнът на бисквитката съвпада с домейна на сървъра, „път“ се използва за посочване на пътя на бисквитката. Ако пътят на бисквитката съвпада с пътя на заявката, бисквитката ще бъде изпратена в заявката. И накрая, както подсказва самото име, стойността на „изтича“ означава времето, когато бисквитките ще изтекат.

Друга важна препоръка е да не се използва по подразбиране името на бисквитката на сесията. Това може да позволи на хакерите да открият сървъра и да изпълнят целенасочена атака. Вместо това използвайте общи имена на бисквитки.

4. Защитете вашите зависимости

Без съмнение npm е мощен инструмент за уеб разработка. За да осигурите най-високо ниво на сигурност, помислете дали да не използвате само 6-тата версия - npm @ 6. По-старите може да съдържат някои сериозни уязвимости в безопасността на зависимостите, които ще застрашат цялото ви приложение. Също така, за да анализирате дървото на зависимостите, използвайте следната команда:

npm audit

npm одитът може да помогне за отстраняване на реални проблеми във вашия проект. Той проверява всички ваши зависимости в зависимости, devDependencies, bundledDependencies и optionalDependencies, но не и вашите peerDependencies. Тук можете да прочетете за всички текущи уязвимости във всички npm пакети.

Securing dependencies

Друг инструмент за осигуряване на безопасността на зависимостите е Snyk. Snyk изпълнява проверката на приложението, за да идентифицира дали съдържа някаква уязвимост, изброена в базата данни с отворен код на Snyk. За да извършите проверката, изпълнете три прости стъпки.

Стъпка 1. Инсталирайте Snyk

npm install -g snyk cd your-app

Стъпка 2. Пуснете тест

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!