Συμβουλές ασφαλείας Express.js: Πώς μπορείτε να αποθηκεύσετε και να ασφαλίσετε την εφαρμογή σας

Ακολουθήστε 7 βήματα για να βεβαιωθείτε ότι η εφαρμογή σας είναι ανίκητη

Είναι κλειδωμένο το τηλέφωνό σας; Έχετε κωδικό PIN, κωδικό πρόσβασης, δακτυλικό αποτύπωμα ή FaceID; Είμαι 99 τοις εκατό σίγουρος ότι το κάνετε. Και είναι σαφές γιατί - σας ενδιαφέρει η ασφάλειά σας. Σήμερα, η προστασία του τηλεφώνου σας είναι εξίσου σημαντική με το βούρτσισμα των δοντιών σας το πρωί.

Για επιμελείς και προσεκτικούς προγραμματιστές λογισμικού, η διατήρηση της εφαρμογής τους ασφαλής είναι εξίσου σημαντική για την προστασία των τηλεφώνων τους. Εάν είστε προγραμματιστής και επιλέξετε να τον παραμελήσετε - παρακαλώ, επανεξετάστε την προσέγγισή σας. Εάν είστε κάτοχος έργου και η ομάδα ανάπτυξης σας λέει ότι η ασφάλεια δεδομένων μπορεί να περιμένει, παρακαλώ, επανεξετάστε την ομάδα σας.

Σε αυτό το άρθρο, θέλω να μιλήσω για το πώς να βεβαιωθώ ότι το έργο Express.js είναι ασφαλές και ανίκητο σε κακόβουλες επιθέσεις.

Υπάρχουν 7 απλά και όχι πολύ απλά μέτρα για την ασφάλεια δεδομένων:

  1. Χρησιμοποιήστε αξιόπιστες εκδόσεις του Express.js
  2. Ασφαλίστε τη σύνδεση και τα δεδομένα
  3. Προστατέψτε τα cookie σας
  4. Εξασφαλίστε τις εξαρτήσεις σας
  5. Επικυρώστε την είσοδο των χρηστών σας
  6. Προστατέψτε το σύστημά σας από ωμή βία
  7. Ελέγξτε την πρόσβαση των χρηστών

Ας ρίξουμε μια πιο προσεκτική ματιά σε καθένα.

1. Χρησιμοποιήστε αξιόπιστες εκδόσεις του Express.js

Οι καταργημένες ή ξεπερασμένες εκδόσεις του Express.js είναι απαγορευμένες. Η 2η και η 3η έκδοση του Express δεν υποστηρίζονται πλέον. Σε αυτά, τα θέματα ασφάλειας ή απόδοσης δεν επιλύονται πλέον.

Ως προγραμματιστής, πρέπει απολύτως να μεταναστεύσετε στο Express 4. Αυτή η έκδοση είναι μια επανάσταση! Είναι αρκετά διαφορετικό όσον αφορά το σύστημα δρομολόγησης, το μεσαίο λογισμικό και άλλες μικρές πτυχές.

2. Ασφαλίστε τη σύνδεση και τα δεδομένα

Για να ασφαλίσετε κεφαλίδες HTTP, μπορείτε να χρησιμοποιήσετε το Helmet.js - μια χρήσιμη μονάδα Node.js. Είναι μια συλλογή από 13 λειτουργίες μεσαίου λογισμικού για τον καθορισμό κεφαλίδων απόκρισης HTTP. Συγκεκριμένα, υπάρχουν λειτουργίες για τον καθορισμό Πολιτικής Ασφάλειας Περιεχομένου, τον χειρισμό Διαφάνειας Πιστοποιητικού, την αποτροπή τζακ-κκ, την απενεργοποίηση της προσωρινής αποθήκευσης από την πλευρά του πελάτη ή την προσθήκη ορισμένων μικρών προστατευτικών XSS.

npm install helmet --save

Ακόμα κι αν δεν θέλετε να χρησιμοποιήσετε όλες τις λειτουργίες του κράνους, το απόλυτο ελάχιστο που πρέπει να κάνετε είναι να απενεργοποιήσετε την κεφαλίδα X-Powered-By:

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

Αυτή η κεφαλίδα μπορεί να χρησιμοποιηθεί για να εντοπίσει ότι η εφαρμογή τροφοδοτείται από το Express, το οποίο επιτρέπει στους εισβολείς να κάνουν μια συγκεκριμένη επίθεση. Σίγουρα, η κεφαλίδα X-Powered-By δεν είναι ο μόνος τρόπος για να προσδιορίσετε μια εφαρμογή που εκτελείται με Express, αλλά είναι ίσως η πιο κοινή και απλή.

Για να προστατεύσετε το σύστημά σας από επιθέσεις ρύπανσης παραμέτρων 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. Προστατέψτε τα cookie σας

Στο Express.js 4, υπάρχουν δύο ενότητες συνεδρίας cookie:

  • express-session (στο Express.js 3, ήταν express.session)
  • cookie-session (στο Express.js 3, ήταν express.cookieSession)

Η ενότητα express-session αποθηκεύει το αναγνωριστικό περιόδου σύνδεσης στο cookie και τα δεδομένα περιόδου σύνδεσης στο διακομιστή. Το cookie-session αποθηκεύει όλα τα δεδομένα περιόδου σύνδεσης στο cookie.

Σε γενικές γραμμές, το cookie-session είναι πιο αποτελεσματικό. Ωστόσο, εάν τα δεδομένα περιόδου σύνδεσης που πρέπει να αποθηκεύσετε είναι πολύπλοκα και ενδέχεται να ξεπερνούν τα 4096 byte ανά cookie, χρησιμοποιήστε το express-session. Ένας άλλος λόγος για τη χρήση του express-session είναι όταν πρέπει να διατηρήσετε τα δεδομένα cookie αόρατα στον πελάτη.

Εκτός αυτού, πρέπει να ορίσετε επιλογές ασφάλειας cookie, δηλαδή:

  • ασφαλής
  • httpΜόνο
  • τομέα
  • μονοπάτι
  • λήγει

Εάν το "ασφαλές" έχει οριστεί σε "true", το πρόγραμμα περιήγησης θα στέλνει cookie μόνο μέσω HTTPS. Εάν το "httpOnly" έχει οριστεί σε "true", το cookie θα σταλεί όχι μέσω του πελάτη JS αλλά μέσω HTTP (S). Η τιμή του "τομέα" υποδηλώνει τον τομέα του cookie. Εάν ο τομέας cookie ταιριάζει με τον τομέα διακομιστή, το "path" χρησιμοποιείται για να υποδείξει τη διαδρομή cookie. Εάν η διαδρομή cookie ταιριάζει με τη διαδρομή αιτήματος, το cookie θα σταλεί στο αίτημα. Τέλος, όπως υποδηλώνει το ίδιο το όνομα, η τιμή "λήγει" σημαίνει το χρόνο λήξης των cookie.

Μια άλλη σημαντική πρόταση δεν είναι να χρησιμοποιήσετε το προεπιλεγμένο όνομα cookie περιόδου σύνδεσης. Μπορεί να επιτρέψει στους χάκερ να εντοπίσουν τον διακομιστή και να εκτελέσουν μια στοχευμένη επίθεση. Αντ 'αυτού, χρησιμοποιήστε γενικά ονόματα cookie.

4. Εξασφαλίστε τις εξαρτήσεις σας

Χωρίς αμφιβολία, το npm είναι ένα ισχυρό εργαλείο ανάπτυξης ιστού. Ωστόσο, για να εξασφαλίσετε το υψηλότερο επίπεδο ασφάλειας, σκεφτείτε να χρησιμοποιήσετε μόνο την 6η έκδοση του - npm @ 6. Τα παλαιότερα ενδέχεται να περιέχουν ορισμένες σοβαρές ευπάθειες ασφάλειας εξάρτησης, οι οποίες θα θέσουν σε κίνδυνο ολόκληρη την εφαρμογή σας. Επίσης, για να αναλύσετε το δέντρο των εξαρτήσεων, χρησιμοποιήστε την ακόλουθη εντολή:

npm audit

Ο έλεγχος npm μπορεί να βοηθήσει στην επίλυση πραγματικών προβλημάτων στο έργο σας. Ελέγχει όλες τις εξαρτήσεις σας σε εξαρτήσεις, devDependencies, bundledDependencies και προαιρετικές εξαρτήσεις, αλλά όχι peerDependencies σας. Εδώ μπορείτε να διαβάσετε για όλες τις τρέχουσες ευπάθειες σε οποιαδήποτε πακέτα npm.

Εξασφάλιση εξαρτήσεων

Ένα άλλο εργαλείο για τη διασφάλιση της ασφάλειας εξάρτησης είναι το 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:

Εξασφάλιση εξαρτήσεων

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.

Μια τεράστια κραυγή στον Volodia Andrushchak, προγραμματιστή λογισμικού Full-Stack @KeenEthics για τη βοήθειά μου στο άρθρο.

Το αρχικό άρθρο που δημοσιεύτηκε στο blog KeenEthics βρίσκεται εδώ: Συμβουλές ασφαλείας Express.js: Αποθηκεύστε την εφαρμογή σας!