सवाल Node.js के साथ एक सुरक्षित REST API को कैसे कार्यान्वित करें


मैं node.js, एक्सप्रेस और mongodb के साथ एक आरईएसटी एपीआई की योजना बनाना शुरू करता हूं। एपीआई एक वेबसाइट (सार्वजनिक और निजी क्षेत्र) और बाद में एक मोबाइल ऐप के लिए डेटा प्रदान करता है। अग्रभाग AngularJS के साथ विकसित किया जाएगा।

कुछ दिनों के लिए मैंने आरईएसटी एपीआई को सुरक्षित करने के बारे में बहुत कुछ पढ़ा, लेकिन मुझे अंतिम समाधान नहीं मिला। जहां तक ​​मैं समझता हूं कि बुनियादी सुरक्षा प्रदान करने के लिए एचटीटीपीएस का उपयोग करना है। लेकिन मैं उस उपयोग के मामलों में एपीआई की रक्षा कैसे कर सकता हूं:

  • वेबसाइट / ऐप के केवल आगंतुक / उपयोगकर्ताओं को वेबसाइट / ऐप के सार्वजनिक क्षेत्र के लिए डेटा प्राप्त करने की अनुमति है

  • केवल प्रमाणित और अधिकृत उपयोगकर्ताओं को निजी क्षेत्र के लिए डेटा प्राप्त करने की अनुमति है (और केवल डेटा, जहां उपयोगकर्ता अनुमति देता है)

फिलहाल मुझे लगता है कि एपीआई का उपयोग करने के लिए सक्रिय सत्र वाले उपयोगकर्ताओं को केवल अनुमति दें। उपयोगकर्ताओं को अधिकृत करने के लिए मैं पासपोर्ट का उपयोग करूंगा और अनुमति के लिए मुझे अपने लिए कुछ लागू करने की आवश्यकता है। सभी एचटीटीपीएस के शीर्ष पर।

क्या कोई कुछ सर्वोत्तम अभ्यास या अनुभव प्रदान कर सकता है? क्या मेरे "आर्किटेक्चर" में कमी है?


195
2018-03-19 10:27


मूल


मैं अनुमान लगा रहा हूं कि एपीआई केवल आपके द्वारा प्रदान किए गए फ्रंटेंड से उपयोग की जा रही है? उस स्थिति में, यह सुनिश्चित करने के लिए सत्र का उपयोग करना कि उपयोगकर्ता वैध है, एक अच्छा समाधान की तरह लगता है। अनुमतियों के लिए, आप एक नज़र डाल सकते हैं नोड भूमिकाओं। - robertklep
आखिरकार आपने इसके लिए क्या किया? कोई बॉयलर प्लेट कोड (सर्वर / मोबाइल ऐप क्लाइंट) आप साझा कर सकते हैं? - Morteza Shahriari Nia


जवाब:


मुझे आपके द्वारा वर्णित एक ही समस्या है। जिस वेबसाइट को मैं बना रहा हूं उसे मोबाइल फोन से और ब्राउजर से एक्सेस किया जा सकता है, इसलिए मुझे उपयोगकर्ताओं को साइन अप करने, लॉगिन करने और कुछ विशिष्ट कार्यों को करने की अनुमति देने के लिए एपीआई की आवश्यकता है। इसके अलावा, मुझे स्केलेबिलिटी का समर्थन करने की आवश्यकता है, एक ही कोड विभिन्न प्रक्रियाओं / मशीनों पर चल रहा है।

चूंकि उपयोगकर्ता संसाधन (उर्फ पोस्ट / पुट क्रियाएं) बना सकते हैं, इसलिए आपको अपनी एपीआई सुरक्षित करने की आवश्यकता है। आप ओथ का उपयोग कर सकते हैं या आप अपना खुद का समाधान बना सकते हैं लेकिन ध्यान रखें कि यदि सभी पासवर्ड खोजना वास्तव में आसान है तो सभी समाधान तोड़ दिए जा सकते हैं। मूलभूत विचार उपयोगकर्ता नाम, पासवर्ड और एक टोकन, उर्फ ​​apitoken का उपयोग कर उपयोगकर्ताओं को प्रमाणित करना है। इस apitoken का उपयोग कर उत्पन्न किया जा सकता है नोड UUID और पासवर्ड का उपयोग कर धोया जा सकता है PBKDF2

फिर, आपको सत्र को कहीं सेव करने की ज़रूरत है। यदि आप इसे एक सादे ऑब्जेक्ट में स्मृति में सहेजते हैं, यदि आप सर्वर को मारते हैं और इसे दोबारा रीबूट करते हैं तो सत्र नष्ट हो जाएगा। इसके अलावा, यह स्केलेबल नहीं है। यदि आप मशीनों के बीच संतुलन लोड करने के लिए हैप्रोक्सी का उपयोग करते हैं या यदि आप केवल श्रमिकों का उपयोग करते हैं, तो यह सत्र स्थिति एक ही प्रक्रिया में संग्रहीत की जाएगी ताकि यदि एक ही उपयोगकर्ता को दूसरी प्रक्रिया / मशीन पर रीडायरेक्ट किया गया हो, तो उसे फिर से प्रमाणीकृत करने की आवश्यकता होगी। इसलिए आपको सत्र को एक सामान्य स्थान पर स्टोर करने की आवश्यकता है। यह आम तौर पर रेडिस का उपयोग करके किया जाता है।

जब उपयोगकर्ता प्रमाणीकृत होता है (उपयोगकर्ता नाम + पासवर्ड + एपिटोकन) सत्र के लिए एक और टोकन उत्पन्न करता है, उर्फ ​​accesstoken। फिर, नोड-यूयूड के साथ। उपयोगकर्ता को accesstoken और उपयोगकर्ता आईडी भेजें। उपयोगकर्ता आईडी (कुंजी) और accesstoken (मान) redis में संग्रहीत किया जाता है और समय समाप्त हो जाता है, उदा। 1 घंटे।

अब, जब भी उपयोगकर्ता शेष एपीआई का उपयोग करके कोई ऑपरेशन करता है तो उसे उपयोगकर्ता आईडी और accesstoken भेजने की आवश्यकता होगी।

यदि आप उपयोगकर्ताओं को बाकी एपीआई का उपयोग करके साइन अप करने की अनुमति देते हैं, तो आपको एक व्यवस्थापक के साथ एक व्यवस्थापक खाता बनाना होगा और उन्हें मोबाइल ऐप में स्टोर करना होगा (उपयोगकर्ता नाम + पासवर्ड + एपिटोकन एन्क्रिप्ट करें) क्योंकि नए उपयोगकर्ताओं को एक स्वीकार्य नहीं होगा वे साइन अप करते हैं।

वेब भी इस एपीआई का उपयोग करता है लेकिन आपको एपिटोकेंस का उपयोग करने की आवश्यकता नहीं है। आप एक रेडिस स्टोर के साथ एक्सप्रेस का उपयोग कर सकते हैं या ऊपर वर्णित एक ही तकनीक का उपयोग कर सकते हैं लेकिन अपरिचित चेक को छोड़कर उपयोगकर्ता को यूजर आईडी + एक कुकी में accesstoken पर लौट सकते हैं।

यदि आपके पास निजी क्षेत्र उपयोगकर्ता नाम की पहचान करते हैं तो वे उपयोगकर्ता द्वारा प्रमाणीकृत करते हैं। आप उपयोगकर्ताओं को भूमिका भी लागू कर सकते हैं।

सारांश:

sequence diagram

बिना किसी विकल्प के एचटीटीपीएस का उपयोग करना होगा और प्राधिकरण शीर्षलेख में उपयोगकर्ता नाम और पासवर्ड भेजना होगा और उपयोगकर्ता नाम को लालसा में कैश करना होगा।


170
2018-03-19 13:25



मैं मोंगोडब का भी उपयोग करता हूं लेकिन यदि आप रेडिस (परमाणु संचालन का उपयोग) का उपयोग कर सत्र (accesstoken) को सहेजते हैं तो इसे प्रबंधित करना बहुत आसान है। जब उपयोगकर्ता खाता बनाता है और इसे वापस उपयोगकर्ता को भेजता है तो सर्वर में apitoken उत्पन्न होता है। फिर, जब उपयोगकर्ता प्रमाणीकृत करना चाहता है तो उसे उपयोगकर्ता नाम + पासवर्ड + एपिटोकन भेजना होगा (उन्हें http body में रखें)। ध्यान रखें कि HTTP शरीर को एन्क्रिप्ट नहीं करता है इसलिए पासवर्ड और एपिटोकन को स्नीफ किया जा सकता है। यदि यह आपके लिए चिंता का विषय है तो HTTPS का उपयोग करें। - Gabriel Llamas
एक का उपयोग करने पर बिंदु क्या है apitoken? क्या यह एक "माध्यमिक" पासवर्ड है? - Salvatorelab
@TheBronx Apitoken में 2 उपयोग के मामले हैं: 1) एक अपरिचित के साथ आप अपने सिस्टम में उपयोगकर्ताओं की पहुंच को नियंत्रित कर सकते हैं और आप प्रत्येक उपयोगकर्ता के आंकड़ों की निगरानी और निर्माण कर सकते हैं। 2) यह एक अतिरिक्त सुरक्षा उपाय है, एक "माध्यमिक" पासवर्ड। - Gabriel Llamas
सफल प्रमाणीकरण के बाद आपको बार-बार उपयोगकर्ता आईडी क्यों भेजनी चाहिए। टोकन एकमात्र रहस्य होना चाहिए जिसे आपको एपीआई कॉल करने की आवश्यकता है। - Axel Napolitano
टोकन का विचार - उपयोगकर्ता गतिविधि को ट्रैक करने के लिए इसका दुरुपयोग करने के अलावा - यह है कि उपयोगकर्ता को आदर्श रूप से किसी एप्लिकेशन का उपयोग करने के लिए किसी उपयोगकर्ता नाम और पासवर्ड की आवश्यकता नहीं होती है: टोकन अद्वितीय पहुंच कुंजी है। यह उपयोगकर्ताओं को किसी भी समय केवल ऐप को प्रभावित करने के लिए किसी भी कुंजी को छोड़ने की अनुमति देता है लेकिन उपयोगकर्ता खाता नहीं। एक webservice के लिए एक टोकन काफी असहनीय है - यही कारण है कि एक सत्र के लिए प्रारंभिक लॉगिन वह जगह है जहां उपयोगकर्ता को टोकन मिलता है - "नियमित" क्लाइंट एबी के लिए, टोकन कोई समस्या नहीं है: इसे एक बार दर्ज करें और आप लगभग पूरा कर चुके हैं ;) - Axel Napolitano


मैं इस कोड को प्रश्न के लिए एक संरचनात्मक समाधान के रूप में योगदान देना चाहता हूं, स्वीकार्य उत्तर के अनुसार (मुझे उम्मीद है)। (आप इसे आसानी से अनुकूलित कर सकते हैं)।

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

इस सर्वर का कर्ल के साथ परीक्षण किया जा सकता है:

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" 

19
2018-05-28 10:38



इस नमूने के लिए धन्यवाद, यह बहुत उपयोगी है, हालांकि मैं इसका पालन करने का प्रयास करता हूं, और जब मैं लॉगिन करने के लिए कनेक्ट करता हूं तो यह कहता है: curl: (51) SSL: प्रमाणपत्र विषय का नाम 'xxxx' लक्ष्य होस्ट नाम 'xxx.net' से मेल नहीं खाता है। मैंने उसी मशीन पर https कनेक्टिंग की अनुमति देने के लिए मेरे / etc / hosts को हार्डकोड किया है - mastervv
महान सामान, स्निपेट के लिए धन्यवाद - does_not_compute


मैंने अभी एक नमूना ऐप समाप्त किया है जो इसे एक बहुत ही बुनियादी, लेकिन स्पष्ट तरीके से करता है। यह उपयोगकर्ताओं को स्टोर और ऑथ प्रबंधन के लिए पासपोर्ट स्टोर करने के लिए मोंगोड के साथ मोंगोज का उपयोग करता है।

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


12
2018-04-02 04:25



आप एपीआई सुरक्षित करने के लिए कुकी का उपयोग कर रहे हैं। मुझे नहीं लगता कि यह सही है। - Vince Yuan


एसओएस पर आरईएसटी औथ पैटर्न के बारे में कई सवाल हैं। ये आपके प्रश्न के लिए सबसे प्रासंगिक हैं:

असल में आपको एपीआई कुंजी का उपयोग करने के बीच चयन करना होगा (एक अनधिकृत उपयोगकर्ता द्वारा कुंजी की खोज की जा सकती है), एक ऐप कुंजी और टोकन कॉम्बो (माध्यम), या एक पूर्ण ओएथ कार्यान्वयन (सबसे सुरक्षित)।


8
2018-03-19 11:34



मैंने oauth 1.0 और oauth 2.0 के बारे में बहुत कुछ पढ़ा है और दोनों संस्करण बहुत सुरक्षित नहीं हैं। विकिपीडिया ने लिखा है कि ओथ 1.0 में कुछ सुरक्षा लीक हैं। इसके अलावा मुझे एक लेख मिला कि कोर डेवलपर में से एक के बारे में टीम छोड़ने के लिए 2.0 को असुरक्षित करना है। - tschiela
@tschiela आपको यहां बताए गए किसी भी चीज़ के संदर्भ जोड़ना चाहिए। - mikemaccana


यदि आप अपने वेबप्लिकेशंस के पूरी तरह से लॉक डाउन एरिया चाहते हैं, जिसे केवल आपकी कंपनी के प्रशासकों द्वारा एक्सेस किया जा सकता है, तो एसएसएल प्रमाणीकरण शायद आपके लिए हो सकता है। यह बीमा करेगा कि कोई भी सर्वर इंस्टेंस से कनेक्शन नहीं बना सकता है जब तक कि उनके पास उनके ब्राउज़र में एक अधिकृत प्रमाणपत्र स्थापित न हो। पिछले हफ्ते मैंने सर्वर को सेटअप करने के तरीके पर एक लेख लिखा था: लेख

यह सबसे सुरक्षित सेटअप में से एक है क्योंकि आप पाएंगे कि कोई उपयोगकर्ता नाम / पासवर्ड शामिल नहीं है, इसलिए कोई भी तब तक पहुंच प्राप्त नहीं कर सकता जब तक कि आपके किसी एक उपयोगकर्ता ने संभावित हैकर को मुख्य फाइलें न हों।


2
2018-03-19 12:57



अच्छा लेख लेकिन निजी क्षेत्र उपयोगकर्ताओं के लिए है। - tschiela
धन्यवाद - ठीक है, तो आपको एक और समाधान के लिए जाना चाहिए, प्रमाण पत्र वितरित करना दर्द होगा। - ExxKA