209 votes

Comment implémenter une API REST sécurisée avec node.js

J'ai commencer la planification d'une API REST avec node.js ,d'exprimer et de mongodb. L'API fournit des données pour un site web (public et privé) et peut-être plus tard d'une application mobile. L'interface sera développé avec AngularJS.

Depuis quelques jours, j'ai beaucoup lu sur la sécurisation des Api REST, mais je ne suis pas une solution définitive. Aussi loin que je comprends, c'est d'utiliser le protocole HTTPS pour fournir une sécurité de base. Mais comment je peux protéger l'API dans ce cas d'utilisation:

  • Seuls les visiteurs/utilisateurs du site/app sont autorisés à obtenir des données pour la partie publique du site web/app

  • Seuls les utilisateurs authentifiés et autorisés sont autorisés à obtenir des données pour le secteur privé (et seulement les données, où l'utilisateur des autorisations)

Au moment où j'y pense pour autoriser uniquement les utilisateurs avec une session active de l'utilisation de l'API. Pour autoriser les utilisateurs, je vais utiliser le passeport et l'autorisation j'ai besoin de mettre en place quelque chose pour moi. Tous les sur le dessus de HTTPS.

Quelqu'un peut-il fournir un certain nombre de meilleures pratiques ou expériences? Est-il d'un manque dans mon "architecture"?

179voto

Gabriel Llamas Points 5808

J'ai eu le même problème que vous décrivez. Le site web que je suis en train de construire peut être accessible à partir d'un téléphone mobile et à partir du navigateur, donc j'ai besoin d'une api pour permettre aux utilisateurs d'inscription, connexion et faire quelques tâches spécifiques. Cette api sera utilisé à partir d'un reste de serveur (téléphone mobile access) et à partir du navigateur. En outre, j'ai besoin de prendre en charge l'évolutivité, le même code en cours d'exécution sur les différents procédés/machines.

Parce que les utilisateurs peuvent CRÉER des ressources (aka POST/PUT actions), vous devez sécuriser votre api. Vous pouvez utiliser oauth ou vous pouvez créer votre propre solution, mais gardez à l'esprit que toutes les solutions peut être rompu si le mot de passe il est vraiment facile à découvrir. L'idée de base est d'authentifier les utilisateurs en utilisant le nom d'utilisateur, mot de passe et un jeton, alias le apitoken. Cette apitoken peut être généré à l'aide de nœud-uuid et le mot de passe peut être haché à l'aide de pbkdf2

Ensuite, vous devez enregistrer la session quelque part. Si vous l'enregistrez dans la mémoire d'un objet ordinaire, si vous tuez le serveur et de le redémarrer à nouveau, la session sera détruite. Aussi, ce n'est pas évolutif. Si vous utilisez haproxy pour équilibrer la charge entre les machines ou tout simplement si vous utilisez des travailleurs, cet état de session seront stockées dans un seul processus, de sorte que si le même utilisateur est redirigé vers un autre processus/de la machine, il aura besoin de s'authentifier à nouveau. Par conséquent, vous avez besoin de stocker la session dans un endroit commun. Ceci est généralement réalisé à l'aide de redis.

Lorsque l'utilisateur est authentifié (nom d'utilisateur+mot de passe+apitoken) générer un autre jeton pour la session, aka accesstoken. Encore une fois, avec nœud-uuid. Envoyer à l'utilisateur la accesstoken et le nom d'utilisateur. Le nom d'utilisateur (clé) et le accesstoken (valeur) sont stockés dans le redis avec et la date d'expiration, par exemple 1h.

Maintenant, chaque fois que l'utilisateur n'a aucune opération à l'aide de l'api rest, il faudra envoyer le nom d'utilisateur et le accesstoken.

Si vous autorisez les utilisateurs à l'inscription à l'aide de l'api rest, vous aurez besoin de créer un compte admin avec un admin apitoken et de les stocker dans l'application mobile (crypter nom d'utilisateur+mot de passe+apitoken) parce que les nouveaux utilisateurs ne disposent pas d'un apitoken quand ils s'inscrivent.

Le web utilise également cette api, mais vous n'avez pas besoin d'utiliser apitokens. Vous pouvez utiliser express avec un redis stocker ou d'utiliser la même technique décrite ci-dessus, mais en contournant le apitoken vérifier et retourner à l'utilisateur le nom d'utilisateur+accesstoken dans un cookie.

Si vous avez des zones privées de comparer le nom d'utilisateur avec les utilisateurs autorisés lors de l'authentification. Vous pouvez également appliquer des rôles aux utilisateurs.

Résumé:

sequence diagram

23voto

cibercitizen1 Points 3093

Je voudrais apporter ce code en tant que solution structurelle à la question posée, en accord (je l'espère) avec la réponse acceptée. (Vous pouvez très facilement le personnaliser).

 // ------------------------------------------------------
// 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);
 

Ce serveur peut être testé avec 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" 
 

11voto

clangager Points 181

Je viens de terminer un exemple d'application qui le fait d'une manière assez simple, mais claire. Il utilise mongoose avec mongodb pour stocker les utilisateurs et passeport pour la gestion des auth.

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

10voto

Skelly Points 27772

Il y a beaucoup de questions au sujet RESTE auth des modèles ici sur. Ce sont les plus pertinentes pour votre question:

Sécurisation de mes Node.js application API REST? Réparateur D'Authentification

Fondamentalement, vous avez besoin de choisir entre l'utilisation de l'API de touches (moins sûr que la clé peut être découvert par un utilisateur non autorisé), une application clé et un jeton combo (moyen), ou un Protocole de mise en œuvre (plus sécurisé).

2voto

ExxKA Points 550

Si vous voulez avoir un cadenas fermé en bas de la zone de votre webapplication qui ne peut être accessible que par les administrateurs de votre société, puis SSL autorisation peut-être pour vous. Il permettra de s'assurer que personne ne peut faire une connexion à une instance de serveur, sauf s'ils ont un certificat autorisé installés dans leur navigateur. La semaine dernière, j'ai écrit un article sur comment configurer le serveur: Article

C'est l'un des plus sécurisé configurations, vous trouverez qu'il n'existe pas de nom d'utilisateur/mots de passe concernés afin que personne ne peut avoir accès, à moins que l'un de vos utilisateurs mains la clé de fichiers à un pirate potentiel.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X