如何使用node.js实现安全的REST API

我开始使用node.js,express和mongodb来规划REST API。 该API为网站(公共和私人区域)提供数据,并可能在稍后提供移动应用程序。 前端将与AngularJS一起开发。

有几天我读了很多关于保护REST API的内容,但是我没有得到最终的解决方案。 据我所知是使用HTTPS提供基本的安全性。 但是,我该如何保护那些用例中的API:

  • 只有网站/应用程序的访问者/用户才能获取网站/应用程序公共区域的数据

  • 只有经过身份验证和授权的用户才能获取私有区域的数据(并且只有用户授予权限的数据)

  • 目前我认为只允许拥有活动会话的用户使用该API。 要授权用户,我将使用护照并获得许可,我需要为自己实施某些功能。 全部位于HTTPS的顶部。

    有人可以提供一些最佳实践或经验吗? 我的“建筑”中是否缺乏?


    我遇到了你描述的同样的问题。 我正在构建的网站可以通过手机和浏览器访问,因此我需要一个API来允许用户注册,登录并执行一些特定的任务。 此外,我需要支持可伸缩性,即在不同进程/机器上运行的相同代码。

    因为用户可以创建资源(aka POST / PUT操作),所以您需要保护您的api。 您可以使用oauth或者您可以构建自己的解决方案,但请记住,如果密码真的很容易发现,所有解决方案都可能被破坏。 基本思想是使用用户名,密码和令牌(也称为apitoken)对用户进行身份验证。 这个apitoken可以使用node-uuid生成,密码可以使用pbkdf2进行散列

    然后,您需要将会话保存在某个地方。 如果你将它保存在一个普通对象的内存中,如果你终止服务器并重新启动它,会话将被销毁。 而且,这不可扩展。 如果您使用haproxy在机器之间进行负载均衡,或者只是简单地使用worker,则会话状态将存储在一个进程中,因此如果同一用户被重定向到另一个进程/机器,则需要重新进行身份验证。 因此,您需要将会话存储在一个普通的地方。 这通常是使用redis完成的。

    当用户通过身份验证(用户名+密码+ apitoken)为会话生成另一个令牌时,也称为accesstoken。 再次,使用node-uuid。 向用户发送accesstoken和用户标识。 userid(key)和accesstoken(value)存储在redis中并且过期时间,例如1h。

    现在,每当用户使用其余api进行任何操作时,它都需要发送用户标识和accesstoken。

    如果您允许用户使用其余api进行注册,则需要创建一个包含管理员apitoken的管理员帐户,并将其存储在移动应用程序中(加密用户名+密码+ apitoken),因为新用户不会在使用apitoken时他们报名参加。

    网络也使用这个API,但你不需要使用apitokens。 您可以使用快递与Redis商店或使用上述相同的技术,但绕过apitoken检查并向用户返回cookie中的userid + accesstoken。

    如果私人区域在验证时将用户名与允许的用户进行比较。 您还可以将角色应用于用户。

    概要:

    没有任何担心的替代方案是使用HTTPS,并在Authorization标头中发送用户名和密码,并在redis中缓存用户名。


    我希望根据(我希望如此)提供的代码作为所提出问题的结构性解决方案,以接受答案。 (你可以很容易地定制它)。

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

    这个服务器可以用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" 
    

    我刚刚完成了一个示例应用程序,它以一种非常基本但清晰的方式来完成此操作。 它使用mongoose和mongodb来存储用户和护照以进行身份​​验证管理。

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

    链接地址: http://www.djcxy.com/p/21953.html

    上一篇: How to implement a secure REST API with node.js

    下一篇: Protect RESTful webservice URL