node.js rest express passport.js

Cómo implementar una API REST segura con node.js



express passport.js (6)

Acabo de terminar una aplicación de muestra que hace esto de una manera bastante básica, pero clara. Utiliza mangosta con mongodb para almacenar usuarios y pasaporte para la administración de autenticación.

https://github.com/Khelldar/Angular-Express-Train-Seed

Comienzo a planificar una API REST con node.js, express y mongodb. La API proporciona datos para un sitio web (área pública y privada) y, más tarde, una aplicación móvil. El frontend se desarrollará con AngularJS.

Durante algunos días leí mucho sobre la protección de las API REST, pero no llego a una solución final. Por lo que entiendo es usar HTTPS para proporcionar una seguridad básica. Pero cómo puedo proteger la API en esos casos de uso:

  • Solo los visitantes / usuarios del sitio web / aplicación pueden obtener datos para el área pública del sitio web / aplicación

  • Solo los usuarios autenticados y autorizados pueden obtener datos para el área privada (y solo los datos donde el usuario otorgó los permisos)

En este momento pienso en permitir que solo los usuarios con una sesión activa utilicen la API. Para autorizar a los usuarios usaré el pasaporte y para obtener permiso, necesito implementar algo para mí. Todo en la parte superior de HTTPS.

¿Alguien puede proporcionar algunas mejores prácticas o experiencias? ¿Hay alguna falta en mi “arquitectura”?


Hay muchas preguntas sobre los patrones de autenticación REST aquí en SO. Estos son los más relevantes para tu pregunta:

Básicamente, debe elegir entre usar las claves de la API (menos seguro ya que la clave puede ser descubierta por un usuario no autorizado), una clave de aplicación y un combo de token (medio), o una implementación completa de OAuth (la más segura).


He tenido el mismo problema que describiste. Se puede acceder al sitio web que estoy creando desde un teléfono móvil y desde el navegador, por lo que necesito una API para que los usuarios puedan registrarse, iniciar sesión y realizar algunas tareas específicas. Además, necesito admitir la escalabilidad, el mismo código que se ejecuta en diferentes procesos / máquinas.

Debido a que los usuarios pueden CREAR recursos (también conocidas como acciones POST / PUT), usted necesita asegurar su api. Puede usar auth o puede crear su propia solución, pero tenga en cuenta que todas las soluciones pueden romperse si la contraseña es realmente fácil de descubrir. La idea básica es autenticar a los usuarios con el nombre de usuario, la contraseña y un token, también conocido como apitoken. Este apitoken se puede generar usando node-uuid y la contraseña se puede hacer hash usando pbkdf2

Entonces, necesitas guardar la sesión en algún lugar. Si lo guarda en la memoria en un objeto simple, si mata el servidor y lo reinicia nuevamente, la sesión se destruirá. Además, esto no es escalable. Si usa haproxy para cargar el equilibrio entre máquinas o si simplemente usa trabajadores, este estado de sesión se almacenará en un solo proceso, por lo que si el mismo usuario se redirige a otro proceso / máquina, deberá autenticarse nuevamente. Por lo tanto, es necesario almacenar la sesión en un lugar común. Esto se hace típicamente utilizando redis.

Cuando el usuario se autentica (nombre de usuario + contraseña + apitoken) genere otro token para la sesión, también conocido como accesstoken. De nuevo, con node-uuid. Enviar al usuario el acceso y el ID de usuario. El ID de usuario (clave) y el acceso (valor) se almacenan en redis con y caducan, por ejemplo, 1 h.

Ahora, cada vez que el usuario realice cualquier operación utilizando la API de descanso, deberá enviar el ID de usuario y el acceso.

Si permite que los usuarios se registren utilizando la API de descanso, deberá crear una cuenta de administrador con una cuenta de administrador y guardarlas en la aplicación móvil (cifrar nombre de usuario + contraseña + apitoken) porque los nuevos usuarios no tendrán una cuenta de apitoken cuando ellos se registran

La web también utiliza esta api, pero no es necesario usar apitokens. Puede usar Express con una tienda redis o usar la misma técnica descrita anteriormente, pero omitiendo el cheque apitoken y devolviendo al usuario el ID de usuario + accesstoken en una cookie.

Si tiene áreas privadas, compare el nombre de usuario con los usuarios permitidos cuando se autentiquen. También puede aplicar roles a los usuarios.

Resumen:

Una alternativa sin apitoken sería usar HTTPS y enviar el nombre de usuario y la contraseña en el encabezado de Autorización y almacenar el nombre de usuario en redis.


Me gustaría contribuir con este código como una solución estructural para la pregunta planteada, de acuerdo (espero que así sea) a la respuesta aceptada. (Se puede personalizar muy fácilmente).

// ------------------------------------------------------ // server.js // ....................................................... // requires var fs = require(''fs''); var express = require(''express''); var myBusinessLogic = require(''../businessLogic/businessLogic.js''); // ....................................................... // security options /* 1. Generate a self-signed certificate-key pair openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out certificate.pem 2. Import them to a keystore (some programs use a keystore) keytool -importcert -file certificate.pem -keystore my.keystore */ var securityOptions = { key: fs.readFileSync(''key.pem''), cert: fs.readFileSync(''certificate.pem''), requestCert: true }; // ....................................................... // create the secure server (HTTPS) var app = express(); var secureServer = require(''https'').createServer(securityOptions, app); // ------------------------------------------------------ // helper functions for auth // ............................................. // true if req == GET /login function isGETLogin (req) { if (req.path != "/login") { return false; } if ( req.method != "GET" ) { return false; } return true; } // () // ............................................. // your auth policy here: // true if req does have permissions // (you may check here permissions and roles // allowed to access the REST action depending // on the URI being accessed) function reqHasPermission (req) { // decode req.accessToken, extract // supposed fields there: userId:roleId:expiryTime // and check them // for the moment we do a very rigorous check if (req.headers.accessToken != "you-are-welcome") { return false; } return true; } // () // ------------------------------------------------------ // install a function to transparently perform the auth check // of incoming request, BEFORE they are actually invoked app.use (function(req, res, next) { if (! isGETLogin (req) ) { if (! reqHasPermission (req) ){ res.writeHead(401); // unauthorized res.end(); return; // don''t call next() } } else { console.log (" * is a login request "); } next(); // continue processing the request }); // ------------------------------------------------------ // copy everything in the req body to req.body app.use (function(req, res, next) { var data=''''; req.setEncoding(''utf8''); req.on(''data'', function(chunk) { data += chunk; }); req.on(''end'', function() { req.body = data; next(); }); }); // ------------------------------------------------------ // REST requests // ------------------------------------------------------ // ....................................................... // authenticating method // GET /login?user=xxx&password=yyy app.get(''/login'', function(req, res){ var user = req.query.user; var password = req.query.password; // rigorous auth check of user-passwrod if (user != "foobar" || password != "1234") { res.writeHead(403); // forbidden } else { // OK: create an access token with fields user, role and expiry time, hash it // and put it on a response header field res.setHeader (''accessToken'', "you-are-welcome"); res.writeHead(200); } res.end(); }); // ....................................................... // "regular" methods (just an example) // newBook() // PUT /book app.put(''/book'', function (req,res){ var bookData = JSON.parse (req.body); myBusinessLogic.newBook(bookData, function (err) { if (err) { res.writeHead(409); res.end(); return; } // no error: res.writeHead(200); res.end(); }); }); // ....................................................... // "main()" secureServer.listen (8081);

Este servidor puede ser probado con curl:

echo "---- first: do login " curl -v "https://localhost:8081/login?user=foobar&password=1234" --cacert certificate.pem # now, in a real case, you should copy the accessToken received before, in the following request echo "---- new book" curl -X POST -d ''{"id": "12341324", "author": "Herman Melville", "title": "Moby-Dick"}'' "https://localhost:8081/book" --cacert certificate.pem --header "accessToken: you-are-welcome"


Si desea asegurar su aplicación, entonces debería comenzar por utilizar HTTPS en lugar de HTTP , esto garantiza la creación de un canal seguro entre usted y los usuarios que evitará rastrear los datos enviados a los usuarios y ayudará a mantener los datos. Intercambio confidencial.

Puede usar JWTs (JSON Web Tokens) para asegurar las API RESTful , esto tiene muchos beneficios en comparación con las sesiones del lado del servidor, los beneficios son principalmente:

1- Más escalable, ya que los servidores de su API no tendrán que mantener sesiones para cada usuario (lo que puede ser una gran carga cuando tiene muchas sesiones)

2- Los JWT son autónomos y tienen las reclamaciones que definen el rol del usuario y qué puede acceder y emitir en la fecha y caducidad (después de lo cual JWT no será válido)

3- Más fácil de manejar en los balanceadores de carga y si tiene múltiples servidores API, ya que no tendrá que compartir datos de sesión ni configurar el servidor para enrutar la sesión al mismo servidor, siempre que una solicitud con un JWT llegue a cualquier servidor, se puede autenticar. y autorizado

4- Menos presión en su base de datos, ya que no tendrá que almacenar y recuperar constantemente la identificación de sesión y los datos para cada solicitud

5- Los JWT no se pueden manipular si utiliza una clave segura para firmar el JWT, por lo que puede confiar en los reclamos en el JWT que se envía con la solicitud sin tener que verificar la sesión del usuario y si está autorizado o no. , solo puede revisar el JWT y luego está listo para saber quién y qué puede hacer este usuario.

Muchas bibliotecas proporcionan formas fáciles de crear y validar JWT en la mayoría de los lenguajes de programación, por ejemplo: en node.js, uno de los más populares es jsonwebtoken

Dado que las API REST generalmente tienen como objetivo mantener el servidor sin estado, los JWT son más compatibles con ese concepto, ya que cada solicitud se envía con un token de Autorización que es autónomo (JWT) sin que el servidor tenga que realizar un seguimiento de las sesiones de usuario en comparación con las sesiones que Servidor con estado para que recuerde al usuario y su rol, sin embargo, las sesiones también se usan ampliamente y tienen sus ventajas, que puede buscar si lo desea.

Una cosa importante a tener en cuenta es que debe entregar el JWT de forma segura al cliente utilizando HTTPS y guardarlo en un lugar seguro (por ejemplo, en el almacenamiento local).

Puedes aprender más sobre JWTs desde este enlace


Si desea tener un área completamente bloqueada de su aplicación web a la que solo puedan acceder los administradores de su empresa, entonces la autorización SSL puede ser para usted. Asegurará que nadie pueda establecer una conexión con la instancia del servidor a menos que tenga un certificado autorizado instalado en su navegador. La semana pasada escribí un artículo sobre cómo configurar el servidor: Article

Esta es una de las configuraciones más seguras que encontrará, ya que no hay nombres de usuario / contraseñas involucrados, por lo que nadie puede obtener acceso a menos que uno de sus usuarios entregue los archivos clave a un pirata informático potencial.